diff --git a/src/Checker/Product/IsProductProcessableChecker.php b/src/Checker/Product/IsProductProcessableChecker.php index 1ee41030..8b82ec86 100644 --- a/src/Checker/Product/IsProductProcessableChecker.php +++ b/src/Checker/Product/IsProductProcessableChecker.php @@ -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; } } diff --git a/src/Exceptions/Retriever/FamilyVariantNotFountException.php b/src/Exceptions/Retriever/FamilyVariantNotFountException.php new file mode 100644 index 00000000..bfba3a6f --- /dev/null +++ b/src/Exceptions/Retriever/FamilyVariantNotFountException.php @@ -0,0 +1,9 @@ +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, ]); diff --git a/src/Processor/Product/AssetProcessor.php b/src/Processor/Product/AssetProcessor.php new file mode 100644 index 00000000..dfbe31fe --- /dev/null +++ b/src/Processor/Product/AssetProcessor.php @@ -0,0 +1,49 @@ +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; + } +} diff --git a/src/Processor/Product/AssetProcessorInterface.php b/src/Processor/Product/AssetProcessorInterface.php new file mode 100644 index 00000000..e3ba90a3 --- /dev/null +++ b/src/Processor/Product/AssetProcessorInterface.php @@ -0,0 +1,9 @@ +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; } } diff --git a/src/Processor/ProductAttribute/AssetAttributeProcessor.php b/src/Processor/ProductAttribute/AssetAttributeProcessor.php index 5e0b0ad6..d2949c28 100644 --- a/src/Processor/ProductAttribute/AssetAttributeProcessor.php +++ b/src/Processor/ProductAttribute/AssetAttributeProcessor.php @@ -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) @@ -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 */ @@ -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); diff --git a/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php b/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php index 8668a0ef..34a73920 100644 --- a/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php +++ b/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php @@ -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, diff --git a/src/Processor/ProductGroup/ProductGroupProcessor.php b/src/Processor/ProductGroup/ProductGroupProcessor.php index eb7c6398..81a22562 100644 --- a/src/Processor/ProductGroup/ProductGroupProcessor.php +++ b/src/Processor/ProductGroup/ProductGroupProcessor.php @@ -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, diff --git a/src/Provider/Asset/AssetValueBuilderProvider.php b/src/Provider/Asset/AssetValueBuilderProvider.php index 748da562..e1da5498 100644 --- a/src/Provider/Asset/AssetValueBuilderProvider.php +++ b/src/Provider/Asset/AssetValueBuilderProvider.php @@ -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, ]); diff --git a/src/Repository/AssetRepository.php b/src/Repository/AssetRepository.php index 026efffe..c8341023 100644 --- a/src/Repository/AssetRepository.php +++ b/src/Repository/AssetRepository.php @@ -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; /** @@ -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(); + } } diff --git a/src/Retriever/FamilyVariantRetriever.php b/src/Retriever/FamilyVariantRetriever.php index 867e54d5..2271d145 100644 --- a/src/Retriever/FamilyVariantRetriever.php +++ b/src/Retriever/FamilyVariantRetriever.php @@ -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 @@ -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'); + } } diff --git a/src/Retriever/FamilyVariantRetrieverInterface.php b/src/Retriever/FamilyVariantRetrieverInterface.php index ee416db2..84f0d65e 100644 --- a/src/Retriever/FamilyVariantRetrieverInterface.php +++ b/src/Retriever/FamilyVariantRetrieverInterface.php @@ -7,4 +7,6 @@ interface FamilyVariantRetrieverInterface { public function getVariants(string $familyCode): array; + + public function getVariant(string $familyCode, string $familyVariantCode): array; } diff --git a/src/Task/AssociationType/ProcessAssociationTypeTask.php b/src/Task/AssociationType/ProcessAssociationTypeTask.php index ee083a50..59490c86 100644 --- a/src/Task/AssociationType/ProcessAssociationTypeTask.php +++ b/src/Task/AssociationType/ProcessAssociationTypeTask.php @@ -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( diff --git a/src/Task/Attribute/ProcessAttributeTask.php b/src/Task/Attribute/ProcessAttributeTask.php index 5778f12f..127d6d26 100644 --- a/src/Task/Attribute/ProcessAttributeTask.php +++ b/src/Task/Attribute/ProcessAttributeTask.php @@ -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()); diff --git a/src/Task/Category/RetrieveCategoriesTask.php b/src/Task/Category/RetrieveCategoriesTask.php index 14b5f88a..23bceb1f 100644 --- a/src/Task/Category/RetrieveCategoriesTask.php +++ b/src/Task/Category/RetrieveCategoriesTask.php @@ -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(), diff --git a/src/Task/Product/ProcessProductsTask.php b/src/Task/Product/ProcessProductsTask.php index 21743ccb..0c11a578 100644 --- a/src/Task/Product/ProcessProductsTask.php +++ b/src/Task/Product/ProcessProductsTask.php @@ -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( diff --git a/src/Task/ProductModel/BatchProductModelTask.php b/src/Task/ProductModel/BatchProductModelTask.php index dfeffc26..a5a1246d 100644 --- a/src/Task/ProductModel/BatchProductModelTask.php +++ b/src/Task/ProductModel/BatchProductModelTask.php @@ -60,7 +60,6 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $this->logger->debug(self::class); $this->type = $payload->getType(); - $this->logger->notice(Messages::createOrUpdate($this->type)); $query = $this->getSelectStatement($payload); $queryResult = $query->executeQuery(); @@ -74,6 +73,10 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte do { try { + $this->logger->notice('Processing product', [ + 'code' => $resource['code'] ?? $resource['identifier'] ?? 'unknown', + ]); + $this->handleProductModel($resource); $isSuccess = true; } catch (ORMInvalidArgumentException $ormInvalidArgumentException) { @@ -130,7 +133,7 @@ private function handleProductGroup(array $resource): void $this->entityManager->flush(); } catch (ORMInvalidArgumentException $ormInvalidArgumentException) { if (!$this->entityManager->isOpen()) { - $this->logger->warning('Recreating entity manager'); + $this->logger->warning('Recreating entity manager', ['exception' => $ormInvalidArgumentException]); $this->entityManager = $this->getNewEntityManager(); } diff --git a/src/Task/ProductModel/ProcessProductModelsTask.php b/src/Task/ProductModel/ProcessProductModelsTask.php index c248c366..39789552 100644 --- a/src/Task/ProductModel/ProcessProductModelsTask.php +++ b/src/Task/ProductModel/ProcessProductModelsTask.php @@ -62,6 +62,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte } $queryParameters = array_merge_recursive($queryParameters, $payload->getCustomFilters()); + $this->logger->notice('Filters', $queryParameters); $resources = $payload->getAkeneoPimClient()->getProductModelApi()->all( $this->apiConnectionProvider->get()->getPaginationSize(),