From 4da43a3012debf1406b9af872761a9135646b8cd Mon Sep 17 00:00:00 2001 From: Nicolas MELONI Date: Thu, 13 Jun 2024 15:14:07 +0200 Subject: [PATCH] refacto filters and category import --- src/Command/AbstractImportCommand.php | 1 + .../BatchImportAssociationTypesCommand.php | 2 + src/Command/BatchImportAttributesCommand.php | 2 + src/Command/BatchImportCategoriesCommand.php | 50 +++++++ .../BatchImportProductModelsCommand.php | 2 + src/Command/BatchImportProductsCommand.php | 2 + src/Command/ImportCategoriesCommand.php | 50 +++---- .../ConfigurationContextInterface.php | 4 + .../ConfigurationContextTrait.php | 14 ++ src/Factory/CategoryPipelineFactory.php | 10 +- src/Factory/PayloadFactory.php | 5 +- .../Task/SymfonyMessengerTaskHandler.php | 5 +- .../Task/SymfonyProcessTaskHandler.php | 5 +- src/Handler/Task/TaskHandlerInterface.php | 3 +- src/Message/Batch/CategoryBatchMessage.php | 12 ++ .../Batch/CategoryBatchMessageHandler.php | 35 +++++ src/Payload/AbstractPayload.php | 1 + src/Payload/Category/CategoryPayload.php | 19 ++- src/Processor/Category/ParentProcessor.php | 2 +- .../Category/CategoryResourceProcessor.php | 130 ++++++++++++++++++ .../AttributeSearchFilterProvider.php | 21 +++ .../Resource/CategorySearchFilterProvider.php | 35 +++++ .../ProductModelSearchFilterProvider.php | 26 ++++ .../Resource/ProductSearchFilterProvider.php | 29 ++++ .../ResourceSearchFilterProviderInterface.php | 16 +++ src/Provider/Filter/SearchFilterProvider.php | 59 ++++++++ .../Filter/SearchFilterProviderInterface.php | 12 ++ .../Task/Batch/CategoryBatchTaskProvider.php | 27 ++++ .../CategoryPayloadBatchTaskProvider.php | 23 ++++ src/Task/Asset/ProcessAssetTask.php | 7 +- .../ProcessAssociationTypeTask.php | 21 +-- src/Task/Attribute/ProcessAttributeTask.php | 24 +--- src/Task/Category/BatchCategoriesTask.php | 60 ++++++++ src/Task/Category/CreateUpdateEntityTask.php | 115 ---------------- ...riesTask.php => ProcessCategoriesTask.php} | 45 +++--- src/Task/Product/ProcessProductsTask.php | 26 +--- .../ProcessProductGroupModelTask.php | 12 +- .../ProductModel/ProcessProductModelsTask.php | 29 +--- src/Task/TaskHandlerTrait.php | 3 +- .../Task/Category/AbstractTaskTest.php | 3 - .../Category/CreateUpdateDeleteTaskTest.php | 36 ++--- .../Category/RetrieveCategoriesTaskTest.php | 47 ++++--- 42 files changed, 707 insertions(+), 323 deletions(-) create mode 100644 src/Command/BatchImportCategoriesCommand.php create mode 100644 src/Message/Batch/CategoryBatchMessage.php create mode 100644 src/MessageHandler/Batch/CategoryBatchMessageHandler.php create mode 100644 src/Processor/Resource/Category/CategoryResourceProcessor.php create mode 100644 src/Provider/Filter/Resource/AttributeSearchFilterProvider.php create mode 100644 src/Provider/Filter/Resource/CategorySearchFilterProvider.php create mode 100644 src/Provider/Filter/Resource/ProductModelSearchFilterProvider.php create mode 100644 src/Provider/Filter/Resource/ProductSearchFilterProvider.php create mode 100644 src/Provider/Filter/Resource/ResourceSearchFilterProviderInterface.php create mode 100644 src/Provider/Filter/SearchFilterProvider.php create mode 100644 src/Provider/Filter/SearchFilterProviderInterface.php create mode 100644 src/Provider/Task/Batch/CategoryBatchTaskProvider.php create mode 100644 src/Provider/Task/Batch/Payload/CommandContext/CategoryPayloadBatchTaskProvider.php create mode 100644 src/Task/Category/BatchCategoriesTask.php delete mode 100644 src/Task/Category/CreateUpdateEntityTask.php rename src/Task/Category/{RetrieveCategoriesTask.php => ProcessCategoriesTask.php} (84%) diff --git a/src/Command/AbstractImportCommand.php b/src/Command/AbstractImportCommand.php index cb53c322..8a00fc42 100644 --- a/src/Command/AbstractImportCommand.php +++ b/src/Command/AbstractImportCommand.php @@ -40,6 +40,7 @@ protected function configure(): void ->addOption('parallel', 'p', InputOption::VALUE_NONE, 'Allow parallel task processing') ->addOption('disable-batch', 'd', InputOption::VALUE_NONE, 'Disable batch processing') ->addOption('batch-size', 's', InputOption::VALUE_OPTIONAL, 'Batch Size', 100) + ->addOption('from-page', null, InputOption::VALUE_OPTIONAL, 'From page', 1) ->addOption('max-concurrency', 'c', InputOption::VALUE_OPTIONAL, 'Max process concurrency', 5) ->addOption('batch-after-fetch', 'a', InputOption::VALUE_OPTIONAL, 'Fetch all pages then start processing the batches', true) ->addOption('filter', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Add filter') diff --git a/src/Command/BatchImportAssociationTypesCommand.php b/src/Command/BatchImportAssociationTypesCommand.php index bfeec8f7..304c5afb 100644 --- a/src/Command/BatchImportAssociationTypesCommand.php +++ b/src/Command/BatchImportAssociationTypesCommand.php @@ -10,6 +10,7 @@ use Synolia\SyliusAkeneoPlugin\Client\ClientFactoryInterface; use Synolia\SyliusAkeneoPlugin\Payload\Association\AssociationTypePayload; use Synolia\SyliusAkeneoPlugin\Task\AssociationType\BatchAssociationTypesTask; +use Webmozart\Assert\Assert; final class BatchImportAssociationTypesCommand extends AbstractBatchCommand { @@ -33,6 +34,7 @@ protected function execute( InputInterface $input, OutputInterface $output, ) { + Assert::string($input->getArgument('ids')); $ids = explode(',', $input->getArgument('ids')); $this->logger->notice('Processing batch', ['from_id' => $ids[0], 'to_id' => $ids[\count($ids) - 1]]); diff --git a/src/Command/BatchImportAttributesCommand.php b/src/Command/BatchImportAttributesCommand.php index 7c1e7b99..4555f192 100644 --- a/src/Command/BatchImportAttributesCommand.php +++ b/src/Command/BatchImportAttributesCommand.php @@ -10,6 +10,7 @@ use Synolia\SyliusAkeneoPlugin\Client\ClientFactoryInterface; use Synolia\SyliusAkeneoPlugin\Payload\Attribute\AttributePayload; use Synolia\SyliusAkeneoPlugin\Task\Attribute\BatchAttributesTask; +use Webmozart\Assert\Assert; final class BatchImportAttributesCommand extends AbstractBatchCommand { @@ -33,6 +34,7 @@ protected function execute( InputInterface $input, OutputInterface $output, ) { + Assert::string($input->getArgument('ids')); $ids = explode(',', $input->getArgument('ids')); $this->logger->notice('Processing batch', ['from_id' => $ids[0], 'to_id' => $ids[\count($ids) - 1]]); diff --git a/src/Command/BatchImportCategoriesCommand.php b/src/Command/BatchImportCategoriesCommand.php new file mode 100644 index 00000000..d41de8f5 --- /dev/null +++ b/src/Command/BatchImportCategoriesCommand.php @@ -0,0 +1,50 @@ +getArgument('ids')); + $ids = explode(',', $input->getArgument('ids')); + + $this->logger->notice('Processing batch', ['from_id' => $ids[0], 'to_id' => $ids[\count($ids) - 1]]); + $this->logger->debug(self::$defaultName, ['batched_ids' => $ids]); + + $payload = new CategoryPayload($this->clientFactory->createFromApiCredentials()); + $payload->setIds($ids); + + $this->task->__invoke($payload); + + return 0; + } +} diff --git a/src/Command/BatchImportProductModelsCommand.php b/src/Command/BatchImportProductModelsCommand.php index 140600e9..1b56de14 100644 --- a/src/Command/BatchImportProductModelsCommand.php +++ b/src/Command/BatchImportProductModelsCommand.php @@ -10,6 +10,7 @@ use Synolia\SyliusAkeneoPlugin\Client\ClientFactoryInterface; use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload; use Synolia\SyliusAkeneoPlugin\Task\ProductModel\BatchProductModelTask; +use Webmozart\Assert\Assert; final class BatchImportProductModelsCommand extends AbstractBatchCommand { @@ -33,6 +34,7 @@ protected function execute( InputInterface $input, OutputInterface $output, ) { + Assert::string($input->getArgument('ids')); $ids = explode(',', $input->getArgument('ids')); $this->logger->notice('Processing batch', ['from_id' => $ids[0], 'to_id' => $ids[\count($ids) - 1]]); diff --git a/src/Command/BatchImportProductsCommand.php b/src/Command/BatchImportProductsCommand.php index f0ba20ec..6a46f57f 100644 --- a/src/Command/BatchImportProductsCommand.php +++ b/src/Command/BatchImportProductsCommand.php @@ -10,6 +10,7 @@ use Synolia\SyliusAkeneoPlugin\Client\ClientFactoryInterface; use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload; use Synolia\SyliusAkeneoPlugin\Task\Product\BatchProductsTask; +use Webmozart\Assert\Assert; final class BatchImportProductsCommand extends AbstractBatchCommand { @@ -33,6 +34,7 @@ protected function execute( InputInterface $input, OutputInterface $output, ) { + Assert::string($input->getArgument('ids')); $ids = explode(',', $input->getArgument('ids')); $this->logger->notice('Processing batch', ['from_id' => $ids[0], 'to_id' => $ids[\count($ids) - 1]]); diff --git a/src/Command/ImportCategoriesCommand.php b/src/Command/ImportCategoriesCommand.php index 52ef5a10..d067b042 100644 --- a/src/Command/ImportCategoriesCommand.php +++ b/src/Command/ImportCategoriesCommand.php @@ -5,60 +5,48 @@ namespace Synolia\SyliusAkeneoPlugin\Command; use Psr\Log\LoggerInterface; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\LockableTrait; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Synolia\SyliusAkeneoPlugin\Client\ClientFactoryInterface; +use Synolia\SyliusAkeneoPlugin\Exceptions\Command\CommandLockedException; use Synolia\SyliusAkeneoPlugin\Factory\CategoryPipelineFactory; -use Synolia\SyliusAkeneoPlugin\Logger\Messages; +use Synolia\SyliusAkeneoPlugin\Factory\PayloadFactoryInterface; use Synolia\SyliusAkeneoPlugin\Payload\Category\CategoryPayload; -final class ImportCategoriesCommand extends Command +final class ImportCategoriesCommand extends AbstractImportCommand { use LockableTrait; - private const DESCRIPTION = 'Import Categories from Akeneo PIM.'; + protected static $defaultDescription = 'Import Categories from Akeneo PIM.'; /** @var string */ protected static $defaultName = 'akeneo:import:categories'; public function __construct( - private CategoryPipelineFactory $categoryPipelineFactory, - private ClientFactoryInterface $clientFactory, - private LoggerInterface $logger, + CategoryPipelineFactory $pipelineFactory, + LoggerInterface $akeneoLogger, + PayloadFactoryInterface $payloadFactory, ) { - parent::__construct(self::$defaultName); - } - - protected function configure(): void - { - $this->setDescription(self::DESCRIPTION); + parent::__construct($akeneoLogger, $payloadFactory, $pipelineFactory, self::$defaultName); } /** * {@inheritdoc} */ - protected function execute( - InputInterface $input, - OutputInterface $output, - ) { - if (!$this->lock()) { - $output->writeln(Messages::commandAlreadyRunning()); - - return 0; - } + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + $this->preExecute(); - $this->logger->notice(self::$defaultName); - /** @var \League\Pipeline\Pipeline $categoryPipeline */ - $categoryPipeline = $this->categoryPipelineFactory->create(); + $payload = $this->payloadFactory->createFromCommand(CategoryPayload::class, $input, $output); + $this->pipeline->process($payload); - /** @var \Synolia\SyliusAkeneoPlugin\Payload\Category\CategoryPayload $categoryPayload */ - $categoryPayload = new CategoryPayload($this->clientFactory->createFromApiCredentials()); - $categoryPipeline->process($categoryPayload); + $this->postExecute(); + } catch (CommandLockedException $commandLockedException) { + $this->logger->warning($commandLockedException->getMessage()); - $this->logger->notice(Messages::endOfCommand(self::$defaultName)); - $this->release(); + return 1; + } return 0; } diff --git a/src/Configuration/ConfigurationContextInterface.php b/src/Configuration/ConfigurationContextInterface.php index 5e0a062d..96bb3183 100644 --- a/src/Configuration/ConfigurationContextInterface.php +++ b/src/Configuration/ConfigurationContextInterface.php @@ -43,4 +43,8 @@ public function setFilters(array $filters): self; public function getHandler(): string; public function setHandler(string $handler): self; + + public function getFromPage(): int; + + public function setFromPage(int $fromPage): self; } diff --git a/src/Configuration/ConfigurationContextTrait.php b/src/Configuration/ConfigurationContextTrait.php index 7672eefe..68081a9c 100644 --- a/src/Configuration/ConfigurationContextTrait.php +++ b/src/Configuration/ConfigurationContextTrait.php @@ -27,6 +27,8 @@ trait ConfigurationContextTrait private string $handler = SymfonyProcessTaskHandler::HANDLER_CODE; + private int $fromPage = 1; + public function getBatchSize(): int { return $this->batchSize; @@ -147,4 +149,16 @@ public function setHandler(string $handler): ConfigurationContextInterface return $this; } + + public function getFromPage(): int + { + return $this->fromPage; + } + + public function setFromPage(int $fromPage): ConfigurationContextInterface + { + $this->fromPage = $fromPage; + + return $this; + } } diff --git a/src/Factory/CategoryPipelineFactory.php b/src/Factory/CategoryPipelineFactory.php index 2e502ae2..4ce52d38 100644 --- a/src/Factory/CategoryPipelineFactory.php +++ b/src/Factory/CategoryPipelineFactory.php @@ -7,8 +7,9 @@ use League\Pipeline\Pipeline; use League\Pipeline\PipelineInterface; use Synolia\SyliusAkeneoPlugin\Pipeline\Processor; -use Synolia\SyliusAkeneoPlugin\Task\Category\CreateUpdateEntityTask; -use Synolia\SyliusAkeneoPlugin\Task\Category\RetrieveCategoriesTask; +use Synolia\SyliusAkeneoPlugin\Task\Category\ProcessCategoriesTask; +use Synolia\SyliusAkeneoPlugin\Task\SetupTask; +use Synolia\SyliusAkeneoPlugin\Task\TearDownTask; final class CategoryPipelineFactory extends AbstractPipelineFactory { @@ -17,8 +18,9 @@ public function create(): PipelineInterface $pipeline = new Pipeline(new Processor($this->dispatcher)); return $pipeline - ->pipe($this->taskProvider->get(RetrieveCategoriesTask::class)) - ->pipe($this->taskProvider->get(CreateUpdateEntityTask::class)) + ->pipe($this->taskProvider->get(SetupTask::class)) + ->pipe($this->taskProvider->get(ProcessCategoriesTask::class)) + ->pipe($this->taskProvider->get(TearDownTask::class)) ; } } diff --git a/src/Factory/PayloadFactory.php b/src/Factory/PayloadFactory.php index eefdd8a0..88717b36 100644 --- a/src/Factory/PayloadFactory.php +++ b/src/Factory/PayloadFactory.php @@ -50,8 +50,9 @@ private function createContext( ->setAllowParallel($isParallelAllowed) ->setBatchingAllowed($isBatchingAllowed) ->setProcessAsSoonAsPossible(filter_var($batchAfterFetch, FILTER_VALIDATE_BOOLEAN)) - ->setBatchSize((int) $input->getOption('batch-size')) - ->setMaxRunningProcessQueueSize((int) $input->getOption('max-concurrency')) + ->setBatchSize((int) $input->getOption('batch-size')) /** @phpstan-ignore-line Cannot cast mixed to int */ + ->setFromPage((int) $input->getOption('from-page')) /** @phpstan-ignore-line Cannot cast mixed to int */ + ->setMaxRunningProcessQueueSize((int) $input->getOption('max-concurrency')) /** @phpstan-ignore-line Cannot cast mixed to int */ ->setFilters((array) ($input->getOption('filter') ?: [])) ->setHandler($input->getOption('handler') ?? $context->getHandler()) ; diff --git a/src/Handler/Task/SymfonyMessengerTaskHandler.php b/src/Handler/Task/SymfonyMessengerTaskHandler.php index 3546765d..fa1f23f4 100644 --- a/src/Handler/Task/SymfonyMessengerTaskHandler.php +++ b/src/Handler/Task/SymfonyMessengerTaskHandler.php @@ -6,7 +6,6 @@ use Akeneo\Pim\ApiClient\Pagination\Page; use Akeneo\Pim\ApiClient\Pagination\PageInterface; -use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Messenger\MessageBusInterface; @@ -37,7 +36,7 @@ public function batch(PipelinePayloadInterface $pipelinePayload, array $items): public function handle( PipelinePayloadInterface $pipelinePayload, - ResourceCursorInterface|PageInterface $handleType, + iterable|PageInterface $handleType, ): void { $count = 0; $items = []; @@ -83,7 +82,7 @@ private function handleByPage( private function handleByCursor( PipelinePayloadInterface $payload, - ResourceCursorInterface $resourceCursor, + iterable $resourceCursor, int &$count = 0, array &$items = [], ): void { diff --git a/src/Handler/Task/SymfonyProcessTaskHandler.php b/src/Handler/Task/SymfonyProcessTaskHandler.php index 1387e310..abc5c155 100644 --- a/src/Handler/Task/SymfonyProcessTaskHandler.php +++ b/src/Handler/Task/SymfonyProcessTaskHandler.php @@ -6,7 +6,6 @@ use Akeneo\Pim\ApiClient\Pagination\Page; use Akeneo\Pim\ApiClient\Pagination\PageInterface; -use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Result; use Doctrine\DBAL\Statement; @@ -114,7 +113,7 @@ public function continue(PipelinePayloadInterface $pipelinePayload): void */ public function handle( PipelinePayloadInterface $pipelinePayload, - ResourceCursorInterface|PageInterface $handleType, + iterable|PageInterface $handleType, ): void { $this->processManager->setInstantProcessing($pipelinePayload->getProcessAsSoonAsPossible()); $this->processManager->setNumberOfParallelProcesses($pipelinePayload->getMaxRunningProcessQueueSize()); @@ -197,7 +196,7 @@ private function handleByPage( private function handleByCursor( PipelinePayloadInterface $payload, - ResourceCursorInterface $resourceCursor, + iterable $resourceCursor, int &$count = 0, array &$ids = [], ): void { diff --git a/src/Handler/Task/TaskHandlerInterface.php b/src/Handler/Task/TaskHandlerInterface.php index 6dae779f..04e0a4a3 100644 --- a/src/Handler/Task/TaskHandlerInterface.php +++ b/src/Handler/Task/TaskHandlerInterface.php @@ -5,7 +5,6 @@ namespace Synolia\SyliusAkeneoPlugin\Handler\Task; use Akeneo\Pim\ApiClient\Pagination\PageInterface; -use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; @@ -25,7 +24,7 @@ public function batch( public function handle( PipelinePayloadInterface $pipelinePayload, - ResourceCursorInterface|PageInterface $handleType, + iterable|PageInterface $handleType, ): void; public function continue(PipelinePayloadInterface $pipelinePayload): void; diff --git a/src/Message/Batch/CategoryBatchMessage.php b/src/Message/Batch/CategoryBatchMessage.php new file mode 100644 index 00000000..dce83bf9 --- /dev/null +++ b/src/Message/Batch/CategoryBatchMessage.php @@ -0,0 +1,12 @@ +items as $resource) { + try { + $this->resourceProcessor->process($resource); + } catch (MaxResourceProcessorRetryException) { + // Skip the failing line + $this->dispatcher->dispatch(new CategoryBatchMessage([$resource])); + + continue; + } + } + } +} diff --git a/src/Payload/AbstractPayload.php b/src/Payload/AbstractPayload.php index b95a83d4..42bef4e6 100644 --- a/src/Payload/AbstractPayload.php +++ b/src/Payload/AbstractPayload.php @@ -39,6 +39,7 @@ public function __construct( $this->isContinue = $commandContext->isContinue(); $this->processAsSoonAsPossible = $commandContext->getProcessAsSoonAsPossible(); $this->handler = $commandContext->getHandler(); + $this->fromPage = $commandContext->getFromPage(); } } diff --git a/src/Payload/Category/CategoryPayload.php b/src/Payload/Category/CategoryPayload.php index 6f650940..d214dc83 100644 --- a/src/Payload/Category/CategoryPayload.php +++ b/src/Payload/Category/CategoryPayload.php @@ -4,12 +4,29 @@ namespace Synolia\SyliusAkeneoPlugin\Payload\Category; +use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; +use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface; use Synolia\SyliusAkeneoPlugin\Exceptions\NoCategoryResourcesException; use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\CategoryBatchMessage; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class CategoryPayload extends AbstractPayload { + public const TEMP_AKENEO_TABLE_NAME = 'tmp_akeneo_categories'; + + public const BATCH_COMMAND_NAME = 'akeneo:batch:categories'; + + public function __construct( + AkeneoPimClientInterface $akeneoPimClient, + ?CommandContextInterface $commandContext = null, + ) { + parent::__construct($akeneoPimClient, $commandContext); + + $this->setTmpTableName(self::TEMP_AKENEO_TABLE_NAME); + $this->setCommandName(self::BATCH_COMMAND_NAME); + } + private array $resources; public function getResources(): array @@ -28,6 +45,6 @@ public function setResources(array $resources): void public function createBatchMessage(array $items): BatchMessageInterface { - throw new \InvalidArgumentException(); + return new CategoryBatchMessage($items); } } diff --git a/src/Processor/Category/ParentProcessor.php b/src/Processor/Category/ParentProcessor.php index 36c680eb..fc52c57f 100644 --- a/src/Processor/Category/ParentProcessor.php +++ b/src/Processor/Category/ParentProcessor.php @@ -35,6 +35,6 @@ public function process(TaxonInterface $taxon, array $resource): void */ public function support(TaxonInterface $taxon, array $resource): bool { - return null !== $resource['parent']; + return array_key_exists('parent', $resource) && null !== $resource['parent']; } } diff --git a/src/Processor/Resource/Category/CategoryResourceProcessor.php b/src/Processor/Resource/Category/CategoryResourceProcessor.php new file mode 100644 index 00000000..cdfa3a45 --- /dev/null +++ b/src/Processor/Resource/Category/CategoryResourceProcessor.php @@ -0,0 +1,130 @@ +retryCount === $this->maxRetryCount) { + $this->retryCount = 0; + + throw new MaxResourceProcessorRetryException(); + } + + if (true === $this->categoryConfigurationProvider->get()->useAkeneoPositions()) { + $this->sortableManager->disableSortableEventListener(); + } + + try { + $this->akeneoLogger->notice('Processing category', [ + 'code' => $resource['code'] ?? 'unknown', + ]); + + $this->dispatcher->dispatch(new BeforeProcessingTaxonEvent($resource)); + + $taxon = $this->getOrCreateEntity($resource['code']); + + $this->processorChain->chain($taxon, $resource); + + $this->dispatcher->dispatch(new AfterProcessingTaxonEvent($resource, $taxon)); + + $this->entityManager->flush(); + + if (true === $this->categoryConfigurationProvider->get()->useAkeneoPositions()) { + $this->sortableManager->enableSortableEventListener(); + } + } catch (ExcludedAttributeException) { + // Do nothing + } catch (ORMInvalidArgumentException $ormInvalidArgumentException) { + ++$this->retryCount; + usleep($this->retryWaitTime); + + $this->akeneoLogger->error('Retrying import', [ + 'product' => $resource, + 'retry_count' => $this->retryCount, + 'error' => $ormInvalidArgumentException->getMessage(), + ]); + + $this->entityManager = $this->getNewEntityManager(); + $this->process($resource); + } catch (\Throwable $throwable) { + ++$this->retryCount; + usleep($this->retryWaitTime); + + $this->akeneoLogger->error('Retrying import', [ + 'message' => $throwable->getMessage(), + 'trace' => $throwable->getTraceAsString(), + ]); + + $this->entityManager = $this->getNewEntityManager(); + $this->process($resource); + } + } + + private function getOrCreateEntity(string $code): TaxonInterface + { + /** @var TaxonInterface $taxon */ + $taxon = $this->taxonRepository->findOneBy(['code' => $code]); + + if (!$taxon instanceof TaxonInterface) { + /** @var TaxonInterface $taxon */ + $taxon = $this->taxonFactory->createNew(); + $taxon->setCode($code); + $this->entityManager->persist($taxon); + + return $taxon; + } + + return $taxon; + } + + private function getNewEntityManager(): EntityManagerInterface + { + $objectManager = $this->managerRegistry->resetManager(); + + if (!$objectManager instanceof EntityManagerInterface) { + throw new \LogicException('Wrong ObjectManager'); + } + + return $objectManager; + } +} diff --git a/src/Provider/Filter/Resource/AttributeSearchFilterProvider.php b/src/Provider/Filter/Resource/AttributeSearchFilterProvider.php new file mode 100644 index 00000000..4cff3eea --- /dev/null +++ b/src/Provider/Filter/Resource/AttributeSearchFilterProvider.php @@ -0,0 +1,21 @@ + true]; + } +} diff --git a/src/Provider/Filter/Resource/CategorySearchFilterProvider.php b/src/Provider/Filter/Resource/CategorySearchFilterProvider.php new file mode 100644 index 00000000..207df6f1 --- /dev/null +++ b/src/Provider/Filter/Resource/CategorySearchFilterProvider.php @@ -0,0 +1,35 @@ +categoryConfigurationProvider->get()->useAkeneoPositions()) { + $queryParameters['with_position'] = true; + } + + return $queryParameters; + } +} diff --git a/src/Provider/Filter/Resource/ProductModelSearchFilterProvider.php b/src/Provider/Filter/Resource/ProductModelSearchFilterProvider.php new file mode 100644 index 00000000..26b858a5 --- /dev/null +++ b/src/Provider/Filter/Resource/ProductModelSearchFilterProvider.php @@ -0,0 +1,26 @@ +productFilter->getProductModelFilters(); + } +} diff --git a/src/Provider/Filter/Resource/ProductSearchFilterProvider.php b/src/Provider/Filter/Resource/ProductSearchFilterProvider.php new file mode 100644 index 00000000..6f9166e4 --- /dev/null +++ b/src/Provider/Filter/Resource/ProductSearchFilterProvider.php @@ -0,0 +1,29 @@ +productFilter->getProductFilters(); + $queryParameters['pagination_type'] = 'search_after'; + + return $queryParameters; + } +} diff --git a/src/Provider/Filter/Resource/ResourceSearchFilterProviderInterface.php b/src/Provider/Filter/Resource/ResourceSearchFilterProviderInterface.php new file mode 100644 index 00000000..0061e6a6 --- /dev/null +++ b/src/Provider/Filter/Resource/ResourceSearchFilterProviderInterface.php @@ -0,0 +1,16 @@ +getBaseQueryParams($payload); + $queryParameters['page'] = $payload->getFromPage(); + + try { + $event = new FilterEvent($payload->getCommandContext()); + $this->eventDispatcher->dispatch($event); + + $queryParameters['search'] = array_merge($queryParameters['search'] ?? [], $event->getFilters()); + } catch (CommandContextIsNullException) { + $queryParameters = []; + } + + $queryParameters = array_merge_recursive($queryParameters, $payload->getCustomFilters()); + + $this->akeneoLogger->notice('Filters', $queryParameters); + + return $queryParameters; + } + + private function getBaseQueryParams(PayloadInterface $payload): array + { + foreach ($this->resourceSearchFilterProviders as $resourceSearchFilterProvider) { + if ($resourceSearchFilterProvider->support($payload)) { + return $resourceSearchFilterProvider->get($payload); + } + } + + return ['search' => []]; + } +} diff --git a/src/Provider/Filter/SearchFilterProviderInterface.php b/src/Provider/Filter/SearchFilterProviderInterface.php new file mode 100644 index 00000000..79c4580d --- /dev/null +++ b/src/Provider/Filter/SearchFilterProviderInterface.php @@ -0,0 +1,12 @@ +task; + } +} diff --git a/src/Provider/Task/Batch/Payload/CommandContext/CategoryPayloadBatchTaskProvider.php b/src/Provider/Task/Batch/Payload/CommandContext/CategoryPayloadBatchTaskProvider.php new file mode 100644 index 00000000..2c805c19 --- /dev/null +++ b/src/Provider/Task/Batch/Payload/CommandContext/CategoryPayloadBatchTaskProvider.php @@ -0,0 +1,23 @@ +hasCommandContext()) ? $payload->getCommandContext() : null; + + return new CategoryPayload($payload->getAkeneoPimClient(), $commandContext); + } + + public function support(PipelinePayloadInterface $pipelinePayload): bool + { + return $pipelinePayload instanceof CategoryPayload; + } +} diff --git a/src/Task/Asset/ProcessAssetTask.php b/src/Task/Asset/ProcessAssetTask.php index 95dd007c..6442a52f 100644 --- a/src/Task/Asset/ProcessAssetTask.php +++ b/src/Task/Asset/ProcessAssetTask.php @@ -9,6 +9,7 @@ use Synolia\SyliusAkeneoPlugin\Logger\Messages; use Synolia\SyliusAkeneoPlugin\Payload\Asset\AssetPayload; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Filter\SearchFilterProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; @@ -22,7 +23,8 @@ final class ProcessAssetTask implements AkeneoTaskInterface public function __construct( private EditionCheckerInterface $editionChecker, private LoggerInterface $akeneoLogger, - private TaskHandlerProviderInterface $taskHandlerProvider, + private SearchFilterProviderInterface $searchFilterProvider, + TaskHandlerProviderInterface $taskHandlerProvider, ) { $this->__taskHandlerConstruct($taskHandlerProvider); } @@ -49,7 +51,8 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte } $this->akeneoLogger->notice(Messages::retrieveFromAPI($payload->getType())); - $resources = $payload->getAkeneoPimClient()->getAssetFamilyApi()->all(); + + $resources = $payload->getAkeneoPimClient()->getAssetFamilyApi()->all($this->searchFilterProvider->get($payload)); $this->handle($payload, $resources); diff --git a/src/Task/AssociationType/ProcessAssociationTypeTask.php b/src/Task/AssociationType/ProcessAssociationTypeTask.php index 6e581d4f..d57403f2 100644 --- a/src/Task/AssociationType/ProcessAssociationTypeTask.php +++ b/src/Task/AssociationType/ProcessAssociationTypeTask.php @@ -5,12 +5,10 @@ namespace Synolia\SyliusAkeneoPlugin\Task\AssociationType; use Psr\Log\LoggerInterface; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Synolia\SyliusAkeneoPlugin\Event\FilterEvent; -use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException; use Synolia\SyliusAkeneoPlugin\Payload\Association\AssociationTypePayload; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Filter\SearchFilterProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; @@ -23,9 +21,9 @@ final class ProcessAssociationTypeTask implements AkeneoTaskInterface public function __construct( private ApiConnectionProviderInterface $apiConnectionProvider, - private EventDispatcherInterface $eventDispatcher, private LoggerInterface $logger, - private TaskHandlerProviderInterface $taskHandlerProvider, + private SearchFilterProviderInterface $searchFilterProvider, + TaskHandlerProviderInterface $taskHandlerProvider, ) { $this->__taskHandlerConstruct($taskHandlerProvider); } @@ -35,7 +33,6 @@ public function __construct( */ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface { - $queryParameters = []; $this->logger->debug(self::class); if ($payload->isContinue()) { @@ -44,20 +41,10 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte return $payload; } - try { - $event = new FilterEvent($payload->getCommandContext()); - $this->eventDispatcher->dispatch($event); - - $queryParameters['search'] = $event->getFilters(); - } catch (CommandContextIsNullException) { - } finally { - $this->logger->notice('Filters', $queryParameters); - } - $page = $payload->getAkeneoPimClient()->getAssociationTypeApi()->listPerPage( $this->apiConnectionProvider->get()->getPaginationSize(), false, - $queryParameters, + $this->searchFilterProvider->get($payload), ); $this->handle($payload, $page); diff --git a/src/Task/Attribute/ProcessAttributeTask.php b/src/Task/Attribute/ProcessAttributeTask.php index 48a77afa..6af77cd9 100644 --- a/src/Task/Attribute/ProcessAttributeTask.php +++ b/src/Task/Attribute/ProcessAttributeTask.php @@ -5,11 +5,9 @@ namespace Synolia\SyliusAkeneoPlugin\Task\Attribute; use Psr\Log\LoggerInterface; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Synolia\SyliusAkeneoPlugin\Event\FilterEvent; -use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Filter\SearchFilterProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; @@ -22,9 +20,9 @@ final class ProcessAttributeTask implements AkeneoTaskInterface public function __construct( private ApiConnectionProviderInterface $apiConnectionProvider, - private EventDispatcherInterface $eventDispatcher, private LoggerInterface $logger, - private TaskHandlerProviderInterface $taskHandlerProvider, + private SearchFilterProviderInterface $searchFilterProvider, + TaskHandlerProviderInterface $taskHandlerProvider, ) { $this->__taskHandlerConstruct($taskHandlerProvider); } @@ -34,7 +32,6 @@ public function __construct( */ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface { - $queryParameters = []; $this->logger->debug(self::class); if ($payload->isContinue()) { @@ -43,23 +40,10 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte return $payload; } - try { - $event = new FilterEvent($payload->getCommandContext()); - $this->eventDispatcher->dispatch($event); - - $queryParameters['search'] = $event->getFilters(); - $queryParameters['with_table_select_options'] = true; - } catch (CommandContextIsNullException) { - } finally { - $this->logger->notice('Filters', $queryParameters); - } - - $queryParameters = \array_merge_recursive($queryParameters, $payload->getCustomFilters()); - $page = $payload->getAkeneoPimClient()->getAttributeApi()->listPerPage( $this->apiConnectionProvider->get()->getPaginationSize(), true, - $queryParameters, + $this->searchFilterProvider->get($payload), ); $this->handle($payload, $page); diff --git a/src/Task/Category/BatchCategoriesTask.php b/src/Task/Category/BatchCategoriesTask.php new file mode 100644 index 00000000..2d7d7035 --- /dev/null +++ b/src/Task/Category/BatchCategoriesTask.php @@ -0,0 +1,60 @@ +akeneoLogger->debug(self::class); + $type = $payload->getType(); + $this->akeneoLogger->notice(Messages::createOrUpdate($type)); + + $query = $this->getSelectStatement($payload); + /** @var Result $queryResult */ + $queryResult = $query->executeQuery(); + + while ($results = $queryResult->fetchAllAssociative()) { + /** @var array{id: int, values: string} $result */ + foreach ($results as $result) { + /** @var array $resource */ + $resource = json_decode($result['values'], true); + + try { + $this->resourceProcessor->process($resource); + $this->removeEntry($payload, (int) $result['id']); + } catch (MaxResourceProcessorRetryException) { + // Skip the failing line + continue; + } + } + } + + return $payload; + } +} diff --git a/src/Task/Category/CreateUpdateEntityTask.php b/src/Task/Category/CreateUpdateEntityTask.php deleted file mode 100644 index f24c4aaa..00000000 --- a/src/Task/Category/CreateUpdateEntityTask.php +++ /dev/null @@ -1,115 +0,0 @@ -logger->debug(self::class); - $this->type = $payload->getType(); - $this->logger->notice(Messages::createOrUpdate($this->type)); - - if (true === $this->categoryConfigurationProvider->get()->useAkeneoPositions()) { - $this->sortableManager->disableSortableEventListener(); - } - - foreach ($payload->getResources() as $resource) { - try { - $this->dispatcher->dispatch(new BeforeProcessingTaxonEvent($resource)); - - if (!$this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->beginTransaction(); - } - - $taxon = $this->getOrCreateEntity($resource['code']); - - $this->processorChain->chain($taxon, $resource); - - $this->dispatcher->dispatch(new AfterProcessingTaxonEvent($resource, $taxon)); - - $this->entityManager->flush(); - - if ($this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->commit(); - } - } catch (Throwable $throwable) { - if ($this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->rollback(); - } - $this->logger->warning($throwable->getMessage()); - } - } - - $this->sortableManager->enableSortableEventListener(); - - $this->logger->notice(Messages::countCreateAndUpdate($this->type, $this->createCount, $this->updateCount)); - - return $payload; - } - - private function getOrCreateEntity(string $code): TaxonInterface - { - /** @var TaxonInterface $taxon */ - $taxon = $this->taxonRepository->findOneBy(['code' => $code]); - - if (!$taxon instanceof TaxonInterface) { - /** @var TaxonInterface $taxon */ - $taxon = $this->taxonFactory->createNew(); - $taxon->setCode($code); - $this->entityManager->persist($taxon); - ++$this->createCount; - $this->logger->info(Messages::hasBeenCreated($this->type, (string) $taxon->getCode())); - - return $taxon; - } - - ++$this->updateCount; - $this->logger->info(Messages::hasBeenUpdated($this->type, (string) $taxon->getCode())); - - return $taxon; - } -} diff --git a/src/Task/Category/RetrieveCategoriesTask.php b/src/Task/Category/ProcessCategoriesTask.php similarity index 84% rename from src/Task/Category/RetrieveCategoriesTask.php rename to src/Task/Category/ProcessCategoriesTask.php index 23bceb1f..02319d08 100644 --- a/src/Task/Category/RetrieveCategoriesTask.php +++ b/src/Task/Category/ProcessCategoriesTask.php @@ -5,27 +5,33 @@ namespace Synolia\SyliusAkeneoPlugin\Task\Category; use Psr\Log\LoggerInterface; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Synolia\SyliusAkeneoPlugin\Event\FilterEvent; -use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException; use Synolia\SyliusAkeneoPlugin\Logger\Messages; use Synolia\SyliusAkeneoPlugin\Payload\Category\CategoryPayload; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\CategoryConfigurationProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Filter\SearchFilterProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; +use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; /** * @internal */ -final class RetrieveCategoriesTask implements AkeneoTaskInterface +final class ProcessCategoriesTask implements AkeneoTaskInterface { + use TaskHandlerTrait{ + TaskHandlerTrait::__construct as private __taskHandlerConstruct; + } + public function __construct( private CategoryConfigurationProviderInterface $categoryConfigurationProvider, private LoggerInterface $logger, private ApiConnectionProviderInterface $apiConnectionProvider, - private EventDispatcherInterface $eventDispatcher, + private SearchFilterProviderInterface $searchFilterProvider, + TaskHandlerProviderInterface $taskHandlerProvider, ) { + $this->__taskHandlerConstruct($taskHandlerProvider); } /** @@ -33,31 +39,12 @@ public function __construct( */ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface { - $queryParameters = []; $this->logger->debug(self::class); $this->logger->notice(Messages::retrieveFromAPI($payload->getType())); - try { - $event = new FilterEvent($payload->getCommandContext()); - $this->eventDispatcher->dispatch($event); - - $queryParameters['search'] = $event->getFilters(); - } catch (CommandContextIsNullException) { - $queryParameters = []; - } - - $queryParameters['with_enriched_attributes'] = true; - - if ($this->categoryConfigurationProvider->get()->useAkeneoPositions()) { - $queryParameters['with_position'] = true; - } - - $queryParameters = \array_merge_recursive($queryParameters, $payload->getCustomFilters()); - $this->logger->notice('Filters', $queryParameters); - $resources = $payload->getAkeneoPimClient()->getCategoryApi()->all( $this->apiConnectionProvider->get()->getPaginationSize(), - $queryParameters, + $this->searchFilterProvider->get($payload), ); $categories = iterator_to_array($resources); @@ -70,6 +57,9 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $excludedCategories = $this->excludeNotImportedCategories($excludedCategoryCodes, $categoriesTree); //Only keep category of the root category + /** + * @var array{code: string} $category + */ foreach ($categories as $key => $category) { if (!\in_array($category['code'], $keptCategories, true)) { $this->logger->info(sprintf('%s: %s is not inside selected root categories and will be excluded', $payload->getType(), $category['code'])); @@ -78,6 +68,9 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte } //Remove excluded categories from kept categories + /** + * @var array{code: string} $category + */ foreach ($categories as $key => $category) { if (\in_array($category['code'], $excludedCategories, true)) { $this->logger->info(sprintf('%s: %s is explicitly excluded from configuration', $payload->getType(), $category['code'])); @@ -91,6 +84,8 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $payload->setResources($categories); + $this->handle($payload, $categories); + return $payload; } diff --git a/src/Task/Product/ProcessProductsTask.php b/src/Task/Product/ProcessProductsTask.php index 280f536f..9af62a72 100644 --- a/src/Task/Product/ProcessProductsTask.php +++ b/src/Task/Product/ProcessProductsTask.php @@ -5,14 +5,11 @@ namespace Synolia\SyliusAkeneoPlugin\Task\Product; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Synolia\SyliusAkeneoPlugin\Event\FilterEvent; -use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException; -use Synolia\SyliusAkeneoPlugin\Filter\ProductFilterInterface; use Synolia\SyliusAkeneoPlugin\Logger\Messages; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Filter\SearchFilterProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; @@ -24,11 +21,10 @@ final class ProcessProductsTask implements AkeneoTaskInterface } public function __construct( - private ProductFilterInterface $productFilter, private ApiConnectionProviderInterface $apiConnectionProvider, - private EventDispatcherInterface $eventDispatcher, private LoggerInterface $logger, - private TaskHandlerProviderInterface $taskHandlerProvider, + private SearchFilterProviderInterface $searchFilterProvider, + TaskHandlerProviderInterface $taskHandlerProvider, ) { $this->__taskHandlerConstruct($taskHandlerProvider); } @@ -48,24 +44,10 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $this->logger->notice(Messages::retrieveFromAPI($payload->getType())); - $queryParameters = $this->productFilter->getProductFilters(); - $queryParameters['pagination_type'] = 'search_after'; - - try { - $event = new FilterEvent($payload->getCommandContext()); - $this->eventDispatcher->dispatch($event); - - $queryParameters['search'] = array_merge($queryParameters['search'] ?? [], $event->getFilters()); - } catch (CommandContextIsNullException) { - } - - $queryParameters = array_merge_recursive($queryParameters, $payload->getCustomFilters()); - $this->logger->notice('Filters', $queryParameters); - $resources = $payload->getAkeneoPimClient()->getProductApi()->listPerPage( $this->apiConnectionProvider->get()->getPaginationSize(), true, - $queryParameters, + $this->searchFilterProvider->get($payload), ); $this->handle($payload, $resources); diff --git a/src/Task/ProductGroup/ProcessProductGroupModelTask.php b/src/Task/ProductGroup/ProcessProductGroupModelTask.php index acf9e38a..e0679a4f 100644 --- a/src/Task/ProductGroup/ProcessProductGroupModelTask.php +++ b/src/Task/ProductGroup/ProcessProductGroupModelTask.php @@ -49,17 +49,17 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte continue; } - /** @var ProductGroupInterface|null $productGroup */ - $productGroup = $this->productGroupRepository->findOneBy(['model' => $resource['parent']]); + /** @var ProductInterface|null $product */ + $product = $this->productRepository->findOneByCode($resource['code']); - if (!$productGroup instanceof ProductGroupInterface) { + if (!$product instanceof ProductInterface) { continue; } - /** @var ProductInterface|null $product */ - $product = $this->productRepository->findOneByCode($resource['code']); + /** @var ProductGroupInterface|null $productGroup */ + $productGroup = $this->productGroupRepository->findOneBy(['model' => $resource['parent']]); - if (!$product instanceof ProductInterface) { + if (!$productGroup instanceof ProductGroupInterface) { continue; } diff --git a/src/Task/ProductModel/ProcessProductModelsTask.php b/src/Task/ProductModel/ProcessProductModelsTask.php index 9fecacaa..822f788e 100644 --- a/src/Task/ProductModel/ProcessProductModelsTask.php +++ b/src/Task/ProductModel/ProcessProductModelsTask.php @@ -5,14 +5,11 @@ namespace Synolia\SyliusAkeneoPlugin\Task\ProductModel; use Psr\Log\LoggerInterface; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Synolia\SyliusAkeneoPlugin\Event\FilterEvent; -use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException; -use Synolia\SyliusAkeneoPlugin\Filter\ProductFilter; use Synolia\SyliusAkeneoPlugin\Logger\Messages; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Filter\SearchFilterProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; @@ -25,11 +22,10 @@ final class ProcessProductModelsTask implements AkeneoTaskInterface } public function __construct( - private ProductFilter $productFilter, private ApiConnectionProviderInterface $apiConnectionProvider, - private EventDispatcherInterface $eventDispatcher, private LoggerInterface $logger, - private TaskHandlerProviderInterface $taskHandlerProvider, + private SearchFilterProviderInterface $searchFilterProvider, + TaskHandlerProviderInterface $taskHandlerProvider, ) { $this->__taskHandlerConstruct($taskHandlerProvider); } @@ -51,23 +47,10 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $this->logger->notice(Messages::retrieveFromAPI($payload->getType())); - $queryParameters = $this->productFilter->getProductModelFilters(); - - try { - $event = new FilterEvent($payload->getCommandContext()); - $this->eventDispatcher->dispatch($event); - - $queryParameters['search'] = array_merge($queryParameters['search'], $event->getFilters()); - } catch (CommandContextIsNullException) { - $queryParameters = []; - } - - $queryParameters = array_merge_recursive($queryParameters, $payload->getCustomFilters()); - $this->logger->notice('Filters', $queryParameters); - - $resources = $payload->getAkeneoPimClient()->getProductModelApi()->all( + $resources = $payload->getAkeneoPimClient()->getProductModelApi()->listPerPage( $this->apiConnectionProvider->get()->getPaginationSize(), - $queryParameters, + true, + $this->searchFilterProvider->get($payload), ); $this->handle($payload, $resources); diff --git a/src/Task/TaskHandlerTrait.php b/src/Task/TaskHandlerTrait.php index 1a5d2d54..623c0a65 100644 --- a/src/Task/TaskHandlerTrait.php +++ b/src/Task/TaskHandlerTrait.php @@ -5,7 +5,6 @@ namespace Synolia\SyliusAkeneoPlugin\Task; use Akeneo\Pim\ApiClient\Pagination\PageInterface; -use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; @@ -23,7 +22,7 @@ protected function continue(PipelinePayloadInterface $pipelinePayload): void protected function handle( PipelinePayloadInterface $pipelinePayload, - ResourceCursorInterface|PageInterface $handleType, + iterable|PageInterface $handleType, ): void { $this->taskHandlerProvider->provide($pipelinePayload)->handle($pipelinePayload, $handleType); } diff --git a/tests/PHPUnit/Task/Category/AbstractTaskTest.php b/tests/PHPUnit/Task/Category/AbstractTaskTest.php index 362e82f6..54ab22ae 100644 --- a/tests/PHPUnit/Task/Category/AbstractTaskTest.php +++ b/tests/PHPUnit/Task/Category/AbstractTaskTest.php @@ -42,9 +42,6 @@ protected function setUp(): void protected function tearDown(): void { - if ($this->manager->getConnection()->isTransactionActive()) { - $this->manager->rollback(); - } $this->manager->close(); $this->manager = null; diff --git a/tests/PHPUnit/Task/Category/CreateUpdateDeleteTaskTest.php b/tests/PHPUnit/Task/Category/CreateUpdateDeleteTaskTest.php index 395908f1..88466847 100644 --- a/tests/PHPUnit/Task/Category/CreateUpdateDeleteTaskTest.php +++ b/tests/PHPUnit/Task/Category/CreateUpdateDeleteTaskTest.php @@ -11,8 +11,8 @@ use Synolia\SyliusAkeneoPlugin\Exceptions\NoCategoryResourcesException; use Synolia\SyliusAkeneoPlugin\Payload\Category\CategoryPayload; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\CategoryConfigurationProviderInterface; -use Synolia\SyliusAkeneoPlugin\Task\Category\CreateUpdateEntityTask; -use Synolia\SyliusAkeneoPlugin\Task\Category\RetrieveCategoriesTask; +use Synolia\SyliusAkeneoPlugin\Task\Category\ProcessCategoriesTask; +use Synolia\SyliusAkeneoPlugin\Task\SetupTask; /** * @internal @@ -35,30 +35,19 @@ protected function setUp(): void $this->categoryConfiguration = $this->buildBasicConfiguration(); } - public function testNoCategories(): void - { - $this->expectExceptionObject(new NoCategoryResourcesException('No resource found.')); - $payload = new CategoryPayload($this->createClient()); - - /** @var CreateUpdateEntityTask $task */ - $task = $this->taskProvider->get(CreateUpdateEntityTask::class); - $task->__invoke($payload); - } - public function testCreateCategories(): void { /** @var CategoryConfigurationProviderInterface $configuration */ $configuration = $this->getContainer()->get(CategoryConfigurationProviderInterface::class); $configuration->get()->setCategoryCodesToImport(['master', 'sales']); - $retrieveCategoryPayload = new CategoryPayload($this->createClient()); + $payload = new CategoryPayload($this->createClient()); - /** @var RetrieveCategoriesTask $task */ - $task = $this->taskProvider->get(RetrieveCategoriesTask::class); - $payload = $task->__invoke($retrieveCategoryPayload); + $setupAttributeTask = $this->taskProvider->get(SetupTask::class); + $payload = $setupAttributeTask->__invoke($payload); - /** @var CreateUpdateEntityTask $task */ - $task = $this->taskProvider->get(CreateUpdateEntityTask::class); + /** @var ProcessCategoriesTask $task */ + $task = $this->taskProvider->get(ProcessCategoriesTask::class); $task->__invoke($payload); $taxonRepository = $this->getContainer()->get('sylius.repository.taxon'); @@ -88,14 +77,13 @@ public function testCreateCategoriesWithRootAndExclusions(): void $this->categoryConfiguration->setNotImportCategories(['clothes_accessories']); $this->manager->flush(); - $retrieveCategoryPayload = new CategoryPayload($this->createClient()); + $payload = new CategoryPayload($this->createClient()); - /** @var RetrieveCategoriesTask $task */ - $task = $this->taskProvider->get(RetrieveCategoriesTask::class); - $payload = $task->__invoke($retrieveCategoryPayload); + $setupAttributeTask = $this->taskProvider->get(SetupTask::class); + $payload = $setupAttributeTask->__invoke($payload); - /** @var CreateUpdateEntityTask $task */ - $task = $this->taskProvider->get(CreateUpdateEntityTask::class); + /** @var ProcessCategoriesTask $task */ + $task = $this->taskProvider->get(ProcessCategoriesTask::class); $task->__invoke($payload); $taxonRepository = $this->getContainer()->get('sylius.repository.taxon'); diff --git a/tests/PHPUnit/Task/Category/RetrieveCategoriesTaskTest.php b/tests/PHPUnit/Task/Category/RetrieveCategoriesTaskTest.php index 4e39d322..ba6c8e2f 100644 --- a/tests/PHPUnit/Task/Category/RetrieveCategoriesTaskTest.php +++ b/tests/PHPUnit/Task/Category/RetrieveCategoriesTaskTest.php @@ -10,7 +10,8 @@ use Symfony\Component\HttpFoundation\Response as HttpResponse; use Synolia\SyliusAkeneoPlugin\Payload\Category\CategoryPayload; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\CategoryConfigurationProviderInterface; -use Synolia\SyliusAkeneoPlugin\Task\Category\RetrieveCategoriesTask; +use Synolia\SyliusAkeneoPlugin\Task\Category\ProcessCategoriesTask; +use Synolia\SyliusAkeneoPlugin\Task\SetupTask; /** * @internal @@ -48,11 +49,14 @@ public function testGetCategories(): void $configuration->get()->setCategoryCodesToImport(['master']); $configuration->get()->setCategoryCodesToExclude([]); - $retrieveCategoryPayload = new CategoryPayload($this->createClient()); + $payload = new CategoryPayload($this->createClient()); - /** @var RetrieveCategoriesTask $task */ - $task = $this->taskProvider->get(RetrieveCategoriesTask::class); - $payload = $task->__invoke($retrieveCategoryPayload); + $setupAttributeTask = $this->taskProvider->get(SetupTask::class); + $payload = $setupAttributeTask->__invoke($payload); + + /** @var ProcessCategoriesTask $task */ + $task = $this->taskProvider->get(ProcessCategoriesTask::class); + $task->__invoke($payload); /** @var array $categoriesTree */ $categoriesTree = $payload->getResources(); @@ -66,11 +70,14 @@ public function testGetCategoriesWithExclusions(): void $configuration->get()->setCategoryCodesToImport(['master']); $configuration->get()->setCategoryCodesToExclude(['sales', 'clothes']); - $retrieveCategoryPayload = new CategoryPayload($this->createClient()); + $payload = new CategoryPayload($this->createClient()); + + $setupAttributeTask = $this->taskProvider->get(SetupTask::class); + $payload = $setupAttributeTask->__invoke($payload); - /** @var RetrieveCategoriesTask $task */ - $task = $this->taskProvider->get(RetrieveCategoriesTask::class); - $payload = $task->__invoke($retrieveCategoryPayload); + /** @var ProcessCategoriesTask $task */ + $task = $this->taskProvider->get(ProcessCategoriesTask::class); + $task->__invoke($payload); $categoriesTree = $payload->getResources(); @@ -103,11 +110,14 @@ public function testGetCategoriesWithRootCategory(): void $configuration->get()->setCategoryCodesToImport(['clothes']); $configuration->get()->setCategoryCodesToExclude([]); - $retrieveCategoryPayload = new CategoryPayload($this->createClient()); + $payload = new CategoryPayload($this->createClient()); - /** @var RetrieveCategoriesTask $task */ - $task = $this->taskProvider->get(RetrieveCategoriesTask::class); - $payload = $task->__invoke($retrieveCategoryPayload); + $setupAttributeTask = $this->taskProvider->get(SetupTask::class); + $payload = $setupAttributeTask->__invoke($payload); + + /** @var ProcessCategoriesTask $task */ + $task = $this->taskProvider->get(ProcessCategoriesTask::class); + $task->__invoke($payload); $categoriesTree = $payload->getResources(); @@ -135,11 +145,14 @@ public function testGetCategoriesWithRootCategoryAndExistingExclusion(): void $configuration->get()->setCategoryCodesToImport(['clothes']); $configuration->get()->setCategoryCodesToExclude(['clothes_accessories']); - $retrieveCategoryPayload = new CategoryPayload($this->createClient()); + $payload = new CategoryPayload($this->createClient()); + + $setupAttributeTask = $this->taskProvider->get(SetupTask::class); + $payload = $setupAttributeTask->__invoke($payload); - /** @var RetrieveCategoriesTask $task */ - $task = $this->taskProvider->get(RetrieveCategoriesTask::class); - $payload = $task->__invoke($retrieveCategoryPayload); + /** @var ProcessCategoriesTask $task */ + $task = $this->taskProvider->get(ProcessCategoriesTask::class); + $task->__invoke($payload); $categoriesTree = $payload->getResources();