Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean removed product assets from Akeneo #183

Merged
merged 2 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 32 additions & 50 deletions src/Checker/Product/IsProductProcessableChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,81 +5,63 @@
namespace Synolia\SyliusAkeneoPlugin\Checker\Product;

use Psr\Log\LoggerInterface;
use Synolia\SyliusAkeneoPlugin\Client\ClientFactoryInterface;
use Synolia\SyliusAkeneoPlugin\Config\AkeneoAxesEnum;
use Synolia\SyliusAkeneoPlugin\Exceptions\Retriever\FamilyVariantNotFountException;
use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface;
use Synolia\SyliusAkeneoPlugin\Retriever\FamilyVariantRetrieverInterface;

final class IsProductProcessableChecker implements IsProductProcessableCheckerInterface
{
private const ONE_VARIATION_AXIS = 1;

private array $familyVariants;

public function __construct(
private ClientFactoryInterface $clientFactory,
private LoggerInterface $logger,
private ApiConnectionProviderInterface $apiConnectionProvider,
private FamilyVariantRetrieverInterface $familyVariantRetriever,
) {
$this->familyVariants = [];
}

public function check(array $resource): bool
{
if ('' === $resource['code'] || null === $resource['code']) {
$this->logger->warning('Skipping product import because the code is missing.', ['resource' => $resource]);

return false;
}
try {
if ('' === $resource['code'] || null === $resource['code']) {
$this->logger->warning('Skipping product import because the code is missing.', ['resource' => $resource]);

if (!isset($resource['family'])) {
$this->logger->warning('Skipping product import because the family is missing.', ['resource' => $resource]);
return false;
}

return false;
}
if (!isset($resource['family'])) {
$this->logger->warning('Skipping product import because the family is missing.', ['resource' => $resource]);

$familyVariantPayload = $this->getFamilyVariant((string) $resource['family'], (string) $resource['family_variant']);
return false;
}

$numberOfVariationAxis = isset($familyVariantPayload['variant_attribute_sets']) ? \count($familyVariantPayload['variant_attribute_sets']) : 0;
$familyVariantPayload = $this->familyVariantRetriever->getVariant((string) $resource['family'], (string) $resource['family_variant']);

if (null === $resource['parent'] &&
$numberOfVariationAxis > self::ONE_VARIATION_AXIS &&
$this->apiConnectionProvider->get()->getAxeAsModel() === AkeneoAxesEnum::FIRST
) {
$this->logger->warning('Skipping product import because the parent is null and it has more than one variation axis.', ['resource' => $resource]);
$numberOfVariationAxis = isset($familyVariantPayload['variant_attribute_sets']) ? \count($familyVariantPayload['variant_attribute_sets']) : 0;

return false;
}
if (null === $resource['parent'] &&
$numberOfVariationAxis > self::ONE_VARIATION_AXIS &&
$this->apiConnectionProvider->get()->getAxeAsModel() === AkeneoAxesEnum::FIRST
) {
$this->logger->debug('Skipping product import because the parent is null and it has more than one variation axis.', ['resource' => $resource]);

// The common model will not be imported. The first axe on akeneo will become the product on sylius and the next axe on akeneo will become an option for the product variant
if (null !== $resource['parent'] &&
$numberOfVariationAxis === 2 &&
$this->apiConnectionProvider->get()->getAxeAsModel() !== AkeneoAxesEnum::FIRST
) {
$this->logger->warning('Skipping product import because the parent is null, and it has more than one variation axis.', ['resource' => $resource]);
return false;
}

return false;
}
// The common model will not be imported. The first axe on akeneo will become the product on sylius and the next axe on akeneo will become an option for the product variant
if (null !== $resource['parent'] &&
$numberOfVariationAxis === 2 &&
$this->apiConnectionProvider->get()->getAxeAsModel() !== AkeneoAxesEnum::FIRST
) {
$this->logger->debug('Skipping product import because the parent is null, and it has more than one variation axis.', ['resource' => $resource]);

return true;
}
return false;
}

private function getFamilyVariant(string $family, string $familyVariant): array
{
if (isset($this->familyVariants[$family][$familyVariant])) {
return $this->familyVariants[$family][$familyVariant];
return true;
} catch (FamilyVariantNotFountException) {
return false;
}

$familyVariantPayload = $this->clientFactory
->createFromApiCredentials()
->getFamilyVariantApi()
->get(
$family,
$familyVariant,
)
;

$this->familyVariants[$family][$familyVariant] = $familyVariantPayload;

return $familyVariantPayload;
}
}
9 changes: 9 additions & 0 deletions src/Exceptions/Retriever/FamilyVariantNotFountException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Synolia\SyliusAkeneoPlugin\Exceptions\Retriever;

class FamilyVariantNotFountException extends \Exception
{
}
8 changes: 7 additions & 1 deletion src/Form/Type/LocalesChoiceType.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ public function __construct(private SyliusAkeneoLocaleCodeProvider $syliusAkeneo

public function configureOptions(OptionsResolver $resolver): void
{
$usedLocalesOnBothPlatforms = $this->syliusAkeneoLocaleCodeProvider->getUsedLocalesOnBothPlatforms();

$usedLocalesOnBothPlatforms = array_map(function ($locale) {
return $this->syliusAkeneoLocaleCodeProvider->getAkeneoLocale($locale);
}, $usedLocalesOnBothPlatforms);

$resolver->setDefaults([
'choices' => $this->syliusAkeneoLocaleCodeProvider->getUsedLocalesOnBothPlatforms(),
'choices' => array_combine($usedLocalesOnBothPlatforms, $usedLocalesOnBothPlatforms),
'multiple' => true,
'required' => false,
]);
Expand Down
49 changes: 49 additions & 0 deletions src/Processor/Product/AssetProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Synolia\SyliusAkeneoPlugin\Processor\Product;

use Sylius\Component\Core\Model\ProductInterface;
use Synolia\SyliusAkeneoPlugin\Checker\EditionCheckerInterface;
use Synolia\SyliusAkeneoPlugin\Repository\AssetRepository;

final class AssetProcessor implements AssetProcessorInterface
{
public static function getDefaultPriority(): int
{
return 850;
}

public function __construct(
private EditionCheckerInterface $editionChecker,
private AssetRepository $assetRepository,
) {
}

public function process(ProductInterface $product, array $resource): void
{
/*
* I need to clean the product-assets association to clean removed product asset from akeneo but
* I couldn't clean assets using $product->getAssets()->clear() before re-importing them as the unit of work still
* thinks that the assets are to be deleted and I didn't want to use the entity manager flush as we don't want
* to import an empty product.
*/
$this->assetRepository->cleanAssetsForProduct($product);
}

public function support(ProductInterface $product, array $resource): bool
{
$isEnterprise = $this->editionChecker->isEnterprise() || $this->editionChecker->isSerenityEdition();

if (!$isEnterprise) {
return false;
}

if (!\method_exists($product, 'getAssets')) {
return false;
}

return true;
}
}
9 changes: 9 additions & 0 deletions src/Processor/Product/AssetProcessorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Synolia\SyliusAkeneoPlugin\Processor\Product;

interface AssetProcessorInterface extends ProductProcessorInterface
{
}
4 changes: 3 additions & 1 deletion src/Processor/Product/ImagesProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ public function support(ProductInterface $product, array $resource): bool
$imageAttributes = $this->getProductConfiguration()->getAkeneoImageAttributes();

if (null === $imageAttributes || 0 === \count($imageAttributes)) {
$this->logger->warning(Messages::noConfigurationSet('at least one Akeneo image attribute', 'Import image'));
$this->logger->debug(Messages::noConfigurationSet('at least one Akeneo image attribute', 'Import image'));

return false;
}

return true;
} catch (\Throwable $throwable) {
$this->logger->warning($throwable->getMessage());

return false;
}
}
Expand Down
13 changes: 10 additions & 3 deletions src/Processor/ProductAttribute/AssetAttributeProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface;
use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider;
use Synolia\SyliusAkeneoPlugin\Transformer\AkeneoAttributeToSyliusAttributeTransformerInterface;
use Webmozart\Assert\Assert;

/**
* @SuppressWarnings(PHPMD.NPathComplexity)
Expand Down Expand Up @@ -54,6 +55,14 @@ public function support(string $attributeCode, array $context = []): bool
return false;
}

if (!$context['model'] instanceof ProductInterface) {
return false;
}

if (!\method_exists($context['model'], 'getAssets')) {
return false;
}

$transformedAttributeCode = $this->akeneoAttributeToSyliusAttributeTransformer->transform($attributeCode);

/** @var AttributeInterface $attribute */
Expand All @@ -74,9 +83,7 @@ public function process(string $attributeCode, array $context = []): void
static::class,
));

if (!$context['model'] instanceof ProductInterface) {
return;
}
Assert::isInstanceOf($context['model'], ProductInterface::class);

$transformedAttributeCode = $this->akeneoAttributeToSyliusAttributeTransformer->transform($attributeCode);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public function process(string $attributeCode, array $context = []): void
$context['scope'],
);
} catch (MissingLocaleTranslationException | MissingLocaleTranslationOrScopeException|MissingScopeException|TranslationNotFoundException $error) {
$this->logger->warning('Attribute translation error', [
$this->logger->debug('Attribute translation error', [
'attribute_code' => $attributeCode,
'sylius_locale' => $syliusLocale,
'context' => $context,
Expand Down
2 changes: 1 addition & 1 deletion src/Processor/ProductGroup/ProductGroupProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private function createGroupForCodeAndFamily(
if ($productGroup instanceof ProductGroupInterface) {
$this->productGroupsMapping[$code] = $productGroup;

$this->logger->info(sprintf(
$this->logger->debug(sprintf(
'Skipping ProductGroup "%s" for family "%s" as it already exists.',
$code,
$family,
Expand Down
2 changes: 1 addition & 1 deletion src/Provider/Asset/AssetValueBuilderProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public function hasSupportedBuilder(string $assetFamilyCode, string $assetCode):
return true;
}
} catch (UnsupportedAttributeTypeException $throwable) {
$this->akeneoLogger->warning('Unsupported AssetAttributeType', [
$this->akeneoLogger->info('Unsupported AssetAttributeType', [
'family_code' => $assetFamilyCode,
'asset_code' => $assetCode,
]);
Expand Down
14 changes: 14 additions & 0 deletions src/Repository/AssetRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

namespace Synolia\SyliusAkeneoPlugin\Repository;

use Doctrine\ORM\Query\ResultSetMapping;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
use Sylius\Component\Core\Model\ProductInterface;
use Synolia\SyliusAkeneoPlugin\Entity\Asset;

/**
Expand All @@ -15,4 +17,16 @@
*/
final class AssetRepository extends EntityRepository
{
public function cleanAssetsForProduct(ProductInterface $product): void
{
$query = $this->_em
->createNativeQuery(
'DELETE FROM akeneo_assets_products WHERE owner_id = :product_id',
new ResultSetMapping(),
)
->setParameter('product_id', $product->getId())
;

$query->execute();
}
}
19 changes: 19 additions & 0 deletions src/Retriever/FamilyVariantRetriever.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Psr\Log\LoggerInterface;
use Symfony\Contracts\Cache\CacheInterface;
use Synolia\SyliusAkeneoPlugin\Component\Cache\CacheKey;
use Synolia\SyliusAkeneoPlugin\Exceptions\Retriever\FamilyVariantNotFountException;
use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface;

final class FamilyVariantRetriever implements FamilyVariantRetrieverInterface
Expand Down Expand Up @@ -44,4 +45,22 @@ public function getVariants(string $familyCode): array
return $familyVariants;
});
}

/**
* @throws FamilyVariantNotFountException
*/
public function getVariant(string $familyCode, string $familyVariantCode): array
{
$familyVariants = $this->getVariants($familyCode);

foreach ($familyVariants as $familyVariant) {
if ($familyVariant['code'] !== $familyVariantCode) {
continue;
}

return $familyVariant;
}

throw new FamilyVariantNotFountException('Could not determine variant');
}
}
2 changes: 2 additions & 0 deletions src/Retriever/FamilyVariantRetrieverInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
interface FamilyVariantRetrieverInterface
{
public function getVariants(string $familyCode): array;

public function getVariant(string $familyCode, string $familyVariantCode): array;
}
3 changes: 2 additions & 1 deletion src/Task/AssociationType/ProcessAssociationTypeTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte

$queryParameters['search'] = $event->getFilters();
} catch (CommandContextIsNullException) {
$queryParameters = [];
} finally {
$this->logger->notice('Filters', $queryParameters);
}

$page = $payload->getAkeneoPimClient()->getAssociationTypeApi()->listPerPage(
Expand Down
3 changes: 2 additions & 1 deletion src/Task/Attribute/ProcessAttributeTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte
$queryParameters['search'] = $event->getFilters();
$queryParameters['with_table_select_options'] = true;
} catch (CommandContextIsNullException) {
$queryParameters = [];
} finally {
$this->logger->notice('Filters', $queryParameters);
}

$queryParameters = \array_merge_recursive($queryParameters, $payload->getCustomFilters());
Expand Down
1 change: 1 addition & 0 deletions src/Task/Category/RetrieveCategoriesTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte
}

$queryParameters = \array_merge_recursive($queryParameters, $payload->getCustomFilters());
$this->logger->notice('Filters', $queryParameters);

$resources = $payload->getAkeneoPimClient()->getCategoryApi()->all(
$this->apiConnectionProvider->get()->getPaginationSize(),
Expand Down
1 change: 1 addition & 0 deletions src/Task/Product/ProcessProductsTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte
}

$queryParameters = array_merge_recursive($queryParameters, $payload->getCustomFilters());
$this->logger->notice('Filters', $queryParameters);

/** @var \Akeneo\Pim\ApiClient\Pagination\PageInterface|null $resources */
$resources = $payload->getAkeneoPimClient()->getProductApi()->listPerPage(
Expand Down
Loading
Loading