diff --git a/install/Application/config/packages/test/akeneo.yaml b/install/Application/config/packages/test/akeneo.yaml index 415951eb..d0662f9f 100644 --- a/install/Application/config/packages/test/akeneo.yaml +++ b/install/Application/config/packages/test/akeneo.yaml @@ -24,7 +24,7 @@ services: Synolia\SyliusAkeneoPlugin\Factory\ProductModelPipelineFactory: ~ Synolia\SyliusAkeneoPlugin\Factory\AssociationTypePipelineFactory: ~ Synolia\SyliusAkeneoPlugin\Filter\ProductFilter: ~ - Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider: ~ + Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider: ~ Synolia\SyliusAkeneoPlugin\Provider\TaskProvider: ~ Synolia\SyliusAkeneoPlugin\Retriever\FamilyRetriever: ~ Synolia\SyliusAkeneoPlugin\Repository\ProductAttributeRepository: ~ diff --git a/ruleset/phpstan-baseline.neon b/ruleset/phpstan-baseline.neon index 87208431..ea1f9f0d 100644 --- a/ruleset/phpstan-baseline.neon +++ b/ruleset/phpstan-baseline.neon @@ -1,771 +1,51 @@ parameters: ignoreErrors: - - message: "#^Cannot call method build\\(\\) on mixed\\.$#" + message: "#^Parameter \\#1 \\$handler of method Synolia\\\\SyliusAkeneoPlugin\\\\Configuration\\\\ConfigurationContextInterface\\:\\:setHandler\\(\\) expects string, mixed given\\.$#" count: 1 - path: ../src/Builder/Attribute/DatabaseProductAttributeValueValueBuilder.php + path: src/Factory/PayloadFactory.php - - message: "#^Parameter \\#2 \\$datetime of static method DateTime\\:\\:createFromFormat\\(\\) expects string, mixed given\\.$#" + message: "#^Call to an undefined method Synolia\\\\SyliusAkeneoPlugin\\\\Handler\\\\Task\\\\SymfonyProcessTaskHandler\\:\\:process\\(\\)\\.$#" count: 1 - path: ../src/Builder/Attribute/DateProductAttributeValueValueBuilder.php - - - - message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" - count: 1 - path: ../src/Builder/Attribute/DateProductAttributeValueValueBuilder.php - - - - message: "#^Cannot cast mixed to int\\.$#" - count: 1 - path: ../src/Builder/Attribute/IntegerProductAttributeValueValueBuilder.php - - - - message: "#^Parameter \\#2 \\$array of function implode expects array\\, mixed given\\.$#" - count: 1 - path: ../src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php - - - - message: "#^Parameter \\#2 \\$array of function implode expects array\\|null, mixed given\\.$#" - count: 1 - path: ../src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php - - - - message: "#^Parameter \\#2 \\$pieces of function implode expects array, mixed given\\.$#" - count: 1 - path: ../src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php - - - - message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: ../src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php - - - - message: "#^Cannot access offset mixed on mixed\\.$#" - count: 1 - path: ../src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php - - - - message: "#^Parameter \\#1 \\$value of method Synolia\\\\SyliusAkeneoPlugin\\\\Transformer\\\\AttributeOptionValueDataTransformerInterface\\:\\:transform\\(\\) expects string, mixed given\\.$#" - count: 1 - path: ../src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php - - - - message: "#^Parameter \\#2 \\$recordCode of method Akeneo\\\\PimEnterprise\\\\ApiClient\\\\Api\\\\ReferenceEntityRecordApiInterface\\:\\:get\\(\\) expects string, mixed given\\.$#" - count: 1 - path: ../src/Builder/Attribute/ReferenceEntityAttributeValueValueBuilder.php - - - - message: "#^Parameter \\#4 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" - count: 1 - path: ../src/Builder/Attribute/ReferenceEntityAttributeValueValueBuilder.php - - - - message: "#^Parameter \\#1 \\$value of method Synolia\\\\SyliusAkeneoPlugin\\\\Transformer\\\\AttributeOptionValueDataTransformerInterface\\:\\:transform\\(\\) expects string, mixed given\\.$#" - count: 1 - path: ../src/Builder/Attribute/SelectProductAttributeValueValueBuilder.php - - - - message: "#^Cannot cast mixed to string\\.$#" - count: 1 - path: ../src/Builder/Attribute/TextProductAttributeValueValueBuilder.php - - - - message: "#^Parameter \\#2 \\$string of function explode expects string, mixed given\\.$#" - count: 1 - path: ../src/Command/BatchImportAssociationTypesCommand.php - - - - message: "#^Parameter \\#2 \\$str of function explode expects string, mixed given\\.$#" - count: 1 - path: ../src/Command/BatchImportAssociationTypesCommand.php - - - - message: "#^Parameter \\#2 \\$string of function explode expects string, mixed given\\.$#" - count: 1 - path: ../src/Command/BatchImportAttributesCommand.php - - - - message: "#^Parameter \\#2 \\$str of function explode expects string, mixed given\\.$#" - count: 1 - path: ../src/Command/BatchImportAttributesCommand.php - - - - message: "#^Parameter \\#2 \\$string of function explode expects string, mixed given\\.$#" - count: 1 - path: ../src/Command/BatchImportFamiliesCommand.php - - - - message: "#^Parameter \\#2 \\$str of function explode expects string, mixed given\\.$#" - count: 1 - path: ../src/Command/BatchImportFamiliesCommand.php - - - - message: "#^Parameter \\#2 \\$string of function explode expects string, mixed given\\.$#" - count: 1 - path: ../src/Command/BatchImportProductModelsCommand.php - - - - message: "#^Parameter \\#2 \\$str of function explode expects string, mixed given\\.$#" - count: 1 - path: ../src/Command/BatchImportProductModelsCommand.php - - - - message: "#^Parameter \\#2 \\$string of function explode expects string, mixed given\\.$#" - count: 1 - path: ../src/Command/BatchImportProductsCommand.php - - - - message: "#^Parameter \\#2 \\$str of function explode expects string, mixed given\\.$#" - count: 1 - path: ../src/Command/BatchImportProductsCommand.php - - - - message: "#^Method Synolia\\\\SyliusAkeneoPlugin\\\\Command\\\\Context\\\\CommandContext\\:\\:isContinue\\(\\) should return bool but returns mixed\\.$#" - count: 1 - path: ../src/Command/Context/CommandContext.php - - - - message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: ../src/Controller/AttributesController.php - - - - message: "#^Cannot access offset 'settings' on mixed\\.$#" - count: 1 - path: ../src/Controller/AttributesController.php - - - - message: "#^Parameter \\#1 \\$attributes of method Synolia\\\\SyliusAkeneoPlugin\\\\Controller\\\\AttributesController\\:\\:addMappedItemsFromFormRequest\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Controller/AttributesController.php - - - - message: "#^Parameter \\#1 \\$attributes of method Synolia\\\\SyliusAkeneoPlugin\\\\Controller\\\\AttributesController\\:\\:removeRemovedMappedItemsFromFormRequest\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Controller/AttributesController.php - - - - message: "#^Parameter \\#1 \\$name of method Synolia\\\\SyliusAkeneoPlugin\\\\Manager\\\\SettingsManagerInterface\\:\\:set\\(\\) expects string, mixed given\\.$#" - count: 1 - path: ../src/Controller/AttributesController.php - - - - message: "#^Parameter \\#1 \\$object of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:persist\\(\\) expects object, mixed given\\.$#" - count: 1 - path: ../src/Controller/CategoriesController.php - - - - message: "#^Parameter \\#1 \\$object of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:persist\\(\\) expects object, mixed given\\.$#" - count: 1 - path: ../src/Controller/ProductFilterRulesController.php - - - - message: "#^Parameter \\#1 \\$object of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:persist\\(\\) expects object, mixed given\\.$#" - count: 1 - path: ../src/Controller/ProductsController.php - - - - message: "#^Parameter \\#1 \\$object of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:remove\\(\\) expects object, mixed given\\.$#" - count: 1 - path: ../src/Controller/ProductsController.php - - - - message: "#^Method Synolia\\\\SyliusAkeneoPlugin\\\\Entity\\\\Asset\\:\\:getProductVariants\\(\\) should return Doctrine\\\\Common\\\\Collections\\\\Collection&iterable\\ but returns Doctrine\\\\Common\\\\Collections\\\\Collection&iterable\\\\.$#" - count: 1 - path: ../src/Entity/Asset.php + path: src/Handler/Task/SymfonyProcessTaskHandler.php - message: "#^Cannot cast mixed to int\\.$#" - count: 2 - path: ../src/Factory/PayloadFactory.php - - - - message: "#^Parameter \\#1 \\$allowParallel of method Synolia\\\\SyliusAkeneoPlugin\\\\Configuration\\\\ConfigurationContextInterface\\:\\:setAllowParallel\\(\\) expects bool, mixed given\\.$#" - count: 1 - path: ../src/Factory/PayloadFactory.php - - - - message: "#^Parameter \\#1 \\$isContinue of method Synolia\\\\SyliusAkeneoPlugin\\\\Command\\\\Context\\\\CommandContext\\:\\:setIsContinue\\(\\) expects bool, mixed given\\.$#" - count: 1 - path: ../src/Factory/PayloadFactory.php - - - - message: "#^Method Synolia\\\\SyliusAkeneoPlugin\\\\Form\\\\DataTransformer\\\\AssetEntityDataTransformer\\:\\:reverseTransform\\(\\) should return array\\|null but returns mixed\\.$#" - count: 1 - path: ../src/Form/DataTransformer/AssetEntityDataTransformer.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#" - count: 1 - path: ../src/Form/DataTransformer/AssetEntityDataTransformer.php - - - - message: "#^Method Synolia\\\\SyliusAkeneoPlugin\\\\Form\\\\DataTransformer\\\\ReferenceEntityDataTransformer\\:\\:reverseTransform\\(\\) should return array\\|null but returns mixed\\.$#" - count: 1 - path: ../src/Form/DataTransformer/ReferenceEntityDataTransformer.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#" - count: 1 - path: ../src/Form/DataTransformer/ReferenceEntityDataTransformer.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 1 - path: ../src/Form/Type/AttributeCodeChoiceType.php - - - - message: "#^Cannot access offset 'labels' on mixed\\.$#" - count: 2 - path: ../src/Form/Type/AttributeCodeChoiceType.php - - - - message: "#^Cannot access offset string on mixed\\.$#" - count: 1 - path: ../src/Form/Type/AttributeCodeChoiceType.php - - - - message: "#^Parameter \\#1 \\$array of function current expects array\\|object, mixed given\\.$#" - count: 1 - path: ../src/Form/Type/AttributeCodeChoiceType.php - - - - message: "#^Parameter \\#1 \\$array_arg of function current expects array\\|object, mixed given\\.$#" - count: 1 - path: ../src/Form/Type/AttributeCodeChoiceType.php - - - - message: "#^Cannot access offset 'settings' on mixed\\.$#" - count: 1 - path: ../src/Form/Type/AttributesTypeMappingType.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 4 - path: ../src/Form/Type/CategoriesChoiceType.php - - - - message: "#^Cannot access offset 'labels' on mixed\\.$#" - count: 2 - path: ../src/Form/Type/CategoriesChoiceType.php - - - - message: "#^Cannot access offset string\\|null on mixed\\.$#" - count: 2 - path: ../src/Form/Type/CategoriesChoiceType.php - - - - message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" - count: 1 - path: ../src/Form/Type/CategoriesChoiceType.php - - - - message: "#^Parameter \\#3 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" - count: 1 - path: ../src/Form/Type/CategoriesChoiceType.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 2 - path: ../src/Form/Type/ChannelChoiceType.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 2 - path: ../src/Form/Type/FamiliesChoiceType.php - - - - message: "#^Cannot call method getAkeneoAttributes\\(\\) on mixed\\.$#" - count: 1 - path: ../src/Processor/AbstractImageProcessor.php - - - - message: "#^Parameter \\#1 \\$content of method Synolia\\\\SyliusAkeneoPlugin\\\\Entity\\\\Asset\\:\\:setContent\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Processor/Asset/Attribute/AkeneoAssetAttributeAttributeProcessor.php - - - - message: "#^Parameter \\#1 \\$name of method Sylius\\\\Component\\\\Product\\\\Generator\\\\SlugGeneratorInterface\\:\\:generate\\(\\) expects string, mixed given\\.$#" - count: 2 - path: ../src/Processor/Product/CompleteRequirementProcessor.php - - - - message: "#^Parameter \\#3 \\$productName of method Synolia\\\\SyliusAkeneoPlugin\\\\Processor\\\\Product\\\\CompleteRequirementProcessor\\:\\:setProductTranslation\\(\\) expects string, mixed given\\.$#" - count: 1 - path: ../src/Processor/Product/CompleteRequirementProcessor.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 1 - path: ../src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php - - - - message: "#^Parameter \\#2 \\$value of method Synolia\\\\SyliusAkeneoPlugin\\\\Transformer\\\\ProductOptionValueDataTransformerInterface\\:\\:transform\\(\\) expects string, mixed given\\.$#" - count: 1 - path: ../src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php - - - - message: "#^Parameter \\#3 \\$record of method Synolia\\\\SyliusAkeneoPlugin\\\\Processor\\\\ProductOptionValue\\\\ReferenceEntityOptionValuesProcessor\\:\\:updateProductOptionValueTranslations\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php - - - - message: "#^Cannot access offset 'data' on mixed\\.$#" - count: 1 - path: ../src/Provider/AkeneoAttributeDataProvider.php - - - - message: "#^Cannot access offset 0 on mixed\\.$#" - count: 1 - path: ../src/Provider/AkeneoAttributeDataProvider.php - - - - message: "#^Parameter \\#2 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\AkeneoAttributeDataProvider\\:\\:getByLocale\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Provider/AkeneoAttributeDataProvider.php - - - - message: "#^Parameter \\#2 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\AkeneoAttributeDataProvider\\:\\:getByLocaleAndScope\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Provider/AkeneoAttributeDataProvider.php - - - - message: "#^Parameter \\#2 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\AkeneoAttributeDataProvider\\:\\:getByScope\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Provider/AkeneoAttributeDataProvider.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 1 - path: ../src/Provider/AkeneoAttributePropertiesProvider.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 1 - path: ../src/Provider/AkeneoFamilyPropertiesProvider.php - - - - message: "#^Cannot access offset 'data' on mixed\\.$#" - count: 1 - path: ../src/Provider/AkeneoReferenceEntityAttributeDataProvider.php - - - - message: "#^Cannot access offset 0 on mixed\\.$#" - count: 1 - path: ../src/Provider/AkeneoReferenceEntityAttributeDataProvider.php - - - - message: "#^Parameter \\#1 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\AkeneoReferenceEntityAttributeDataProvider\\:\\:getByLocale\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Provider/AkeneoReferenceEntityAttributeDataProvider.php - - - - message: "#^Parameter \\#1 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\AkeneoReferenceEntityAttributeDataProvider\\:\\:getByLocaleAndScope\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Provider/AkeneoReferenceEntityAttributeDataProvider.php - - - - message: "#^Parameter \\#1 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\AkeneoReferenceEntityAttributeDataProvider\\:\\:getByScope\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Provider/AkeneoReferenceEntityAttributeDataProvider.php - - - - message: "#^Access to an undefined property Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\Asset\\\\AkeneoAssetAttributeDataProvider\\:\\:\\$assetAttributeValueBuilder\\.$#" count: 3 - path: ../src/Provider/Asset/AkeneoAssetAttributeDataProvider.php - - - - message: "#^Cannot access offset 'data' on mixed\\.$#" - count: 1 - path: ../src/Provider/Asset/AkeneoAssetAttributeDataProvider.php - - - - message: "#^Cannot access offset 0 on mixed\\.$#" - count: 1 - path: ../src/Provider/Asset/AkeneoAssetAttributeDataProvider.php - - - - message: "#^Parameter \\#3 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\Asset\\\\AkeneoAssetAttributeDataProvider\\:\\:getByLocale\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Provider/Asset/AkeneoAssetAttributeDataProvider.php - - - - message: "#^Parameter \\#3 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\Asset\\\\AkeneoAssetAttributeDataProvider\\:\\:getByLocaleAndScope\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Provider/Asset/AkeneoAssetAttributeDataProvider.php - - - - message: "#^Parameter \\#3 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\Asset\\\\AkeneoAssetAttributeDataProvider\\:\\:getByScope\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Provider/Asset/AkeneoAssetAttributeDataProvider.php - - - - message: "#^Call to an undefined method Akeneo\\\\Pim\\\\ApiClient\\\\AkeneoPimClientInterface\\:\\:getAssetAttributeApi\\(\\)\\.$#" - count: 2 - path: ../src/Provider/Asset/AkeneoAssetAttributePropertiesProvider.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 3 - path: ../src/Provider/SyliusAkeneoLocaleCodeProvider.php - - - - message: "#^Cannot access offset 'enabled' on mixed\\.$#" - count: 1 - path: ../src/Provider/SyliusAkeneoLocaleCodeProvider.php - - - - message: "#^Property Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\SyliusAkeneoLocaleCodeProvider\\:\\:\\$localesCode \\(array\\\\) does not accept array\\.$#" - count: 1 - path: ../src/Provider/SyliusAkeneoLocaleCodeProvider.php - - - - message: "#^Method Synolia\\\\SyliusAkeneoPlugin\\\\Repository\\\\ChannelRepository\\:\\:findByCurrencyCode\\(\\) should return iterable but returns mixed\\.$#" - count: 1 - path: ../src/Repository/ChannelRepository.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 1 - path: ../src/Repository/LocaleRepository.php - - - - message: "#^Parameter \\#2 \\$array of function array_map expects array, mixed given\\.$#" - count: 1 - path: ../src/Repository/LocaleRepository.php - - - - message: "#^Method Synolia\\\\SyliusAkeneoPlugin\\\\Repository\\\\ProductAttributeRepository\\:\\:findByCodes\\(\\) should return array\\ but returns mixed\\.$#" - count: 1 - path: ../src/Repository/ProductAttributeRepository.php - - - - message: "#^Method Synolia\\\\SyliusAkeneoPlugin\\\\Repository\\\\ProductAttributeRepository\\:\\:getMissingAttributesIds\\(\\) should return array\\ but returns mixed\\.$#" - count: 1 - path: ../src/Repository/ProductAttributeRepository.php - - - - message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(mixed\\)\\: mixed\\)\\|null, Closure\\(array\\)\\: mixed given\\.$#" - count: 1 - path: ../src/Repository/ProductAttributeRepository.php - - - - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#" - count: 1 - path: ../src/Repository/ProductAttributeRepository.php - - - - message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, mixed given\\.$#" - count: 1 - path: ../src/Repository/ProductAttributeRepository.php - - - - message: "#^Parameter \\#2 \\$array of function array_map expects array, mixed given\\.$#" - count: 1 - path: ../src/Repository/ProductAttributeRepository.php - - - - message: "#^Cannot cast mixed to int\\.$#" - count: 1 - path: ../src/Repository/ProductGroupRepository.php - - - - message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(mixed\\)\\: mixed\\)\\|null, Closure\\(array\\)\\: mixed given\\.$#" - count: 1 - path: ../src/Repository/ProductOptionRepository.php - - - - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#" - count: 1 - path: ../src/Repository/ProductOptionRepository.php - - - - message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, mixed given\\.$#" - count: 1 - path: ../src/Repository/ProductOptionRepository.php - - - - message: "#^Parameter \\#2 \\$array of function array_map expects array, mixed given\\.$#" - count: 1 - path: ../src/Repository/ProductOptionRepository.php - - - - message: "#^Method Synolia\\\\SyliusAkeneoPlugin\\\\Repository\\\\ProductRepository\\:\\:findProductsUsingCategories\\(\\) should return iterable but returns mixed\\.$#" - count: 1 - path: ../src/Repository/ProductRepository.php - - - - message: "#^Method Synolia\\\\SyliusAkeneoPlugin\\\\Repository\\\\TaxonRepository\\:\\:getMissingCategoriesIds\\(\\) should return iterable but returns mixed\\.$#" - count: 1 - path: ../src/Repository/TaxonRepository.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 4 - path: ../src/Retriever/FamilyRetriever.php - - - - message: "#^Method Synolia\\\\SyliusAkeneoPlugin\\\\Retriever\\\\FamilyRetriever\\:\\:getFamilyCodeByVariantCode\\(\\) should return string but returns mixed\\.$#" - count: 1 - path: ../src/Retriever/FamilyRetriever.php - - - - message: "#^Parameter \\#1 \\$familyCode of method Akeneo\\\\Pim\\\\ApiClient\\\\Api\\\\FamilyVariantApiInterface\\:\\:all\\(\\) expects string, mixed given\\.$#" - count: 1 - path: ../src/Retriever/FamilyRetriever.php - - - - message: "#^Property Synolia\\\\SyliusAkeneoPlugin\\\\Retriever\\\\FamilyRetriever\\:\\:\\$familiesByVariant \\(array\\\\) does not accept array\\.$#" - count: 1 - path: ../src/Retriever/FamilyRetriever.php - - - - message: "#^Cannot access offset 'id' on mixed\\.$#" - count: 2 - path: ../src/Task/AbstractProcessTask.php - - - - message: "#^Cannot cast mixed to int\\.$#" - count: 3 - path: ../src/Task/AbstractProcessTask.php + path: src/Handler/Task/SymfonyProcessTaskHandler.php - message: "#^Parameter \\#1 \\$array of function current expects array\\|object, mixed given\\.$#" count: 2 - path: ../src/Task/AbstractProcessTask.php - - - - message: "#^Parameter \\#1 \\$array_arg of function current expects array\\|object, mixed given\\.$#" - count: 2 - path: ../src/Task/AbstractProcessTask.php - - - - message: "#^Cannot access offset 'id' on mixed\\.$#" - count: 2 - path: ../src/Task/Asset/BatchAssetTask.php + path: src/Handler/Task/SymfonyProcessTaskHandler.php - - message: "#^Cannot access offset 'values' on mixed\\.$#" + message: "#^Parameter \\#2 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\Data\\\\AkeneoAttributeDataProvider\\:\\:getByLocale\\(\\) expects array, mixed given\\.$#" count: 1 - path: ../src/Task/Asset/BatchAssetTask.php + path: src/Provider/Data/AkeneoAttributeDataProvider.php - - message: "#^Cannot cast mixed to int\\.$#" - count: 2 - path: ../src/Task/Asset/BatchAssetTask.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#" - count: 1 - path: ../src/Task/Asset/BatchAssetTask.php - - - - message: "#^Parameter \\#2 \\$assetFamilyResource of method Synolia\\\\SyliusAkeneoPlugin\\\\Task\\\\Asset\\\\BatchAssetTask\\:\\:retrieveAssets\\(\\) expects array, mixed given\\.$#" + message: "#^Parameter \\#2 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\Data\\\\AkeneoAttributeDataProvider\\:\\:getByLocaleAndScope\\(\\) expects array, mixed given\\.$#" count: 1 - path: ../src/Task/Asset/BatchAssetTask.php + path: src/Provider/Data/AkeneoAttributeDataProvider.php - - message: "#^Parameter \\#2 \\$assetResource of method Synolia\\\\SyliusAkeneoPlugin\\\\Task\\\\Asset\\\\BatchAssetTask\\:\\:handleAssetByFamilyResource\\(\\) expects array, mixed given\\.$#" + message: "#^Parameter \\#2 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\Data\\\\AkeneoAttributeDataProvider\\:\\:getByScope\\(\\) expects array, mixed given\\.$#" count: 1 - path: ../src/Task/Asset/BatchAssetTask.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 2 - path: ../src/Task/AssociationType/BatchAssociationTypesTask.php - - - - message: "#^Cannot access offset 'id' on mixed\\.$#" - count: 2 - path: ../src/Task/AssociationType/BatchAssociationTypesTask.php - - - - message: "#^Cannot access offset 'values' on mixed\\.$#" - count: 1 - path: ../src/Task/AssociationType/BatchAssociationTypesTask.php - - - - message: "#^Cannot cast mixed to int\\.$#" - count: 2 - path: ../src/Task/AssociationType/BatchAssociationTypesTask.php - - - - message: "#^Parameter \\#1 \\$code of method Sylius\\\\Component\\\\Resource\\\\Model\\\\CodeAwareInterface\\:\\:setCode\\(\\) expects string\\|null, mixed given\\.$#" - count: 1 - path: ../src/Task/AssociationType/BatchAssociationTypesTask.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#" - count: 1 - path: ../src/Task/AssociationType/BatchAssociationTypesTask.php - - - - message: "#^Parameter \\#1 \\$resource of method Synolia\\\\SyliusAkeneoPlugin\\\\Task\\\\AssociationType\\\\BatchAssociationTypesTask\\:\\:addTranslations\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Task/AssociationType/BatchAssociationTypesTask.php - - - - message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Cannot access offset 'axes' on mixed\\.$#" - count: 1 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 1 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Cannot access offset 'id' on mixed\\.$#" - count: 2 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Cannot access offset 'values' on mixed\\.$#" - count: 1 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Cannot access offset 'variant_attributeā€¦' on mixed\\.$#" - count: 2 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Cannot access offset 0 on mixed\\.$#" - count: 1 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Cannot cast mixed to int\\.$#" - count: 2 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Parameter \\#1 \\$array of function usort expects array, mixed given\\.$#" - count: 1 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Parameter \\#1 \\$array_arg of function usort expects array, mixed given\\.$#" - count: 1 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Parameter \\#1 \\$familyCode of method Akeneo\\\\Pim\\\\ApiClient\\\\Api\\\\FamilyVariantApiInterface\\:\\:all\\(\\) expects string, mixed given\\.$#" - count: 1 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#" - count: 1 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Parameter \\#1 \\$resource of class Synolia\\\\SyliusAkeneoPlugin\\\\Event\\\\Attribute\\\\BeforeProcessingAttributeEvent constructor expects array, mixed given\\.$#" - count: 1 - path: ../src/Task/Attribute/BatchAttributesTask.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 1 - path: ../src/Task/Attribute/RetrieveAttributesTask.php - - - - message: "#^Parameter \\#2 \\$value of static method Synolia\\\\SyliusAkeneoPlugin\\\\Logger\\\\Messages\\:\\:totalToImport\\(\\) expects int, mixed given\\.$#" - count: 1 - path: ../src/Task/Attribute/RetrieveAttributesTask.php - - - - message: "#^Cannot access offset 'code' on mixed\\.$#" - count: 4 - path: ../src/Task/Category/RetrieveCategoriesTask.php - - - - message: "#^Parameter \\#3 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" - count: 2 - path: ../src/Task/Category/RetrieveCategoriesTask.php - - - - message: "#^Cannot access offset 'id' on mixed\\.$#" - count: 2 - path: ../src/Task/Family/BatchFamilyTask.php - - - - message: "#^Cannot access offset 'values' on mixed\\.$#" - count: 1 - path: ../src/Task/Family/BatchFamilyTask.php - - - - message: "#^Cannot cast mixed to int\\.$#" - count: 2 - path: ../src/Task/Family/BatchFamilyTask.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#" - count: 1 - path: ../src/Task/Family/BatchFamilyTask.php - - - - message: "#^Parameter \\#1 \\$resource of method Synolia\\\\SyliusAkeneoPlugin\\\\Processor\\\\ProductGroup\\\\FamilyVariationAxeProcessor\\:\\:process\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Task/Family/BatchFamilyTask.php - - - - message: "#^Parameter \\#1 \\$resource of method Synolia\\\\SyliusAkeneoPlugin\\\\Task\\\\Family\\\\BatchFamilyTask\\:\\:createProductGroups\\(\\) expects array, mixed given\\.$#" - count: 1 - path: ../src/Task/Family/BatchFamilyTask.php - - - - message: "#^Cannot access offset 'id' on mixed\\.$#" - count: 2 - path: ../src/Task/Product/BatchProductsTask.php - - - - message: "#^Cannot access offset 'values' on mixed\\.$#" - count: 1 - path: ../src/Task/Product/BatchProductsTask.php - - - - message: "#^Cannot cast mixed to int\\.$#" - count: 2 - path: ../src/Task/Product/BatchProductsTask.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#" - count: 1 - path: ../src/Task/Product/BatchProductsTask.php - - - - message: "#^Cannot access offset 'id' on mixed\\.$#" - count: 2 - path: ../src/Task/ProductModel/BatchProductModelTask.php - - - - message: "#^Cannot access offset 'values' on mixed\\.$#" - count: 1 - path: ../src/Task/ProductModel/BatchProductModelTask.php - - - - message: "#^Cannot cast mixed to int\\.$#" - count: 2 - path: ../src/Task/ProductModel/BatchProductModelTask.php + path: src/Provider/Data/AkeneoAttributeDataProvider.php - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#" + message: "#^Parameter \\#1 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\Data\\\\AkeneoReferenceEntityAttributeDataProvider\\:\\:getByLocale\\(\\) expects array, mixed given\\.$#" count: 1 - path: ../src/Task/ProductModel/BatchProductModelTask.php + path: src/Provider/Data/AkeneoReferenceEntityAttributeDataProvider.php - - message: "#^Parameter \\#1 \\$resource of class Synolia\\\\SyliusAkeneoPlugin\\\\Event\\\\Product\\\\BeforeProcessingProductEvent constructor expects array, mixed given\\.$#" + message: "#^Parameter \\#1 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\Data\\\\AkeneoReferenceEntityAttributeDataProvider\\:\\:getByLocaleAndScope\\(\\) expects array, mixed given\\.$#" count: 1 - path: ../src/Task/ProductModel/BatchProductModelTask.php + path: src/Provider/Data/AkeneoReferenceEntityAttributeDataProvider.php - - message: "#^Parameter \\#1 \\$resource of method Synolia\\\\SyliusAkeneoPlugin\\\\Checker\\\\Product\\\\IsProductProcessableCheckerInterface\\:\\:check\\(\\) expects array, mixed given\\.$#" + message: "#^Parameter \\#1 \\$attributeValues of method Synolia\\\\SyliusAkeneoPlugin\\\\Provider\\\\Data\\\\AkeneoReferenceEntityAttributeDataProvider\\:\\:getByScope\\(\\) expects array, mixed given\\.$#" count: 1 - path: ../src/Task/ProductModel/BatchProductModelTask.php + path: src/Provider/Data/AkeneoReferenceEntityAttributeDataProvider.php diff --git a/src/Builder/Attribute/AssetAttributeValueValueBuilder.php b/src/Builder/Attribute/AssetAttributeValueValueBuilder.php index be11737e..479d6a33 100644 --- a/src/Builder/Attribute/AssetAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/AssetAttributeValueValueBuilder.php @@ -4,7 +4,7 @@ namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AssetCollectionAttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; diff --git a/src/Builder/Attribute/BooleanProductAttributeValueValueBuilder.php b/src/Builder/Attribute/BooleanProductAttributeValueValueBuilder.php index ddd10346..e776ed3f 100644 --- a/src/Builder/Attribute/BooleanProductAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/BooleanProductAttributeValueValueBuilder.php @@ -4,7 +4,7 @@ namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\BooleanAttributeTypeMatcher; diff --git a/src/Builder/Attribute/CollectionReferenceEntityAttributeValueValueBuilder.php b/src/Builder/Attribute/CollectionReferenceEntityAttributeValueValueBuilder.php index 833517ad..dc3d9455 100644 --- a/src/Builder/Attribute/CollectionReferenceEntityAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/CollectionReferenceEntityAttributeValueValueBuilder.php @@ -4,7 +4,7 @@ namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\CollectionReferenceEntityAttributeTypeMatcher; diff --git a/src/Builder/Attribute/DatabaseProductAttributeValueValueBuilder.php b/src/Builder/Attribute/DatabaseProductAttributeValueValueBuilder.php index 53e5da5a..fb11e315 100644 --- a/src/Builder/Attribute/DatabaseProductAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/DatabaseProductAttributeValueValueBuilder.php @@ -4,7 +4,7 @@ namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\DatabaseMappingAttributeTypeMatcher; diff --git a/src/Builder/Attribute/DateProductAttributeValueValueBuilder.php b/src/Builder/Attribute/DateProductAttributeValueValueBuilder.php index ab8e372e..c46ad860 100644 --- a/src/Builder/Attribute/DateProductAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/DateProductAttributeValueValueBuilder.php @@ -7,7 +7,7 @@ use DateTime; use DateTimeInterface; use LogicException; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\DateAttributeTypeMatcher; diff --git a/src/Builder/Attribute/IntegerProductAttributeValueValueBuilder.php b/src/Builder/Attribute/IntegerProductAttributeValueValueBuilder.php index 9b687afe..7907a1d3 100644 --- a/src/Builder/Attribute/IntegerProductAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/IntegerProductAttributeValueValueBuilder.php @@ -4,7 +4,7 @@ namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\IntegerAttributeTypeMatcher; diff --git a/src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php b/src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php index 1243cad8..4d92fc3b 100644 --- a/src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/MetricProductAttributeValueValueBuilder.php @@ -4,7 +4,7 @@ namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\MetricAttributeTypeMatcher; diff --git a/src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php b/src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php index 0c60bbb7..41a94320 100644 --- a/src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/MultiSelectProductAttributeValueValueBuilder.php @@ -4,7 +4,7 @@ namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Transformer\AttributeOptionValueDataTransformerInterface; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\MultiSelectAttributeTypeMatcher; diff --git a/src/Builder/Attribute/ReferenceEntityAttributeValueValueBuilder.php b/src/Builder/Attribute/ReferenceEntityAttributeValueValueBuilder.php index 0cda57d2..7207750f 100644 --- a/src/Builder/Attribute/ReferenceEntityAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/ReferenceEntityAttributeValueValueBuilder.php @@ -11,8 +11,8 @@ use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoReferenceEntityAttributeDataProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoReferenceEntityAttributeDataProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\ProductRefEntityAttributeValueValueBuilderProviderInterface; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\ReferenceEntityAttributeTypeMatcher; diff --git a/src/Builder/Attribute/SelectProductAttributeValueValueBuilder.php b/src/Builder/Attribute/SelectProductAttributeValueValueBuilder.php index 75797ec1..ebc2f267 100644 --- a/src/Builder/Attribute/SelectProductAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/SelectProductAttributeValueValueBuilder.php @@ -4,7 +4,7 @@ namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Transformer\AttributeOptionValueDataTransformerInterface; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\SelectAttributeTypeMatcher; diff --git a/src/Builder/Attribute/TableAttributeValueValueBuilder.php b/src/Builder/Attribute/TableAttributeValueValueBuilder.php index 2492cc0b..c0b3614b 100644 --- a/src/Builder/Attribute/TableAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/TableAttributeValueValueBuilder.php @@ -7,7 +7,7 @@ use Sylius\Component\Product\Model\ProductAttributeInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; use Synolia\SyliusAkeneoPlugin\Exceptions\Processor\MissingAkeneoProductAttributeValueProcessorException; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\ProductAttributeValue\TableAttributeValueProcessorProviderInterface; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\TableAttributeTypeMatcher; diff --git a/src/Builder/Attribute/TextProductAttributeValueValueBuilder.php b/src/Builder/Attribute/TextProductAttributeValueValueBuilder.php index 1e003999..f3c5992d 100644 --- a/src/Builder/Attribute/TextProductAttributeValueValueBuilder.php +++ b/src/Builder/Attribute/TextProductAttributeValueValueBuilder.php @@ -4,7 +4,7 @@ namespace Synolia\SyliusAkeneoPlugin\Builder\Attribute; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\TextareaAttributeTypeMatcher; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\TextAttributeTypeMatcher; diff --git a/src/Builder/ProductOptionValue/NonLocalizableOptionValueBuilder.php b/src/Builder/ProductOptionValue/NonLocalizableOptionValueBuilder.php index afbc8369..f9b256f8 100644 --- a/src/Builder/ProductOptionValue/NonLocalizableOptionValueBuilder.php +++ b/src/Builder/ProductOptionValue/NonLocalizableOptionValueBuilder.php @@ -13,7 +13,7 @@ use Synolia\SyliusAkeneoPlugin\Event\ProductOptionValueTranslation\AfterProcessingProductOptionValueTranslationEvent; use Synolia\SyliusAkeneoPlugin\Event\ProductOptionValueTranslation\BeforeProcessingProductOptionValueTranslationEvent; use Synolia\SyliusAkeneoPlugin\Exceptions\Builder\ProductOptionValueTranslation\ProductOptionValueTranslationBuilderNotFoundException; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider; use Synolia\SyliusAkeneoPlugin\Transformer\ProductOptionValueDataTransformerInterface; use Webmozart\Assert\Assert; diff --git a/src/Builder/ProductOptionValueTranslation/MetricProductOptionValueTranslationBuilder.php b/src/Builder/ProductOptionValueTranslation/MetricProductOptionValueTranslationBuilder.php index cc00a2af..7fbfe656 100644 --- a/src/Builder/ProductOptionValueTranslation/MetricProductOptionValueTranslationBuilder.php +++ b/src/Builder/ProductOptionValueTranslation/MetricProductOptionValueTranslationBuilder.php @@ -14,8 +14,8 @@ use Synolia\SyliusAkeneoPlugin\Exceptions\Retriever\FamilyMeasureNotFoundException; use Synolia\SyliusAkeneoPlugin\Exceptions\Retriever\MeasurableNotFoundException; use Synolia\SyliusAkeneoPlugin\Exceptions\UnsupportedAttributeTypeException; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\ProductFilterRulesProviderInterface; use Synolia\SyliusAkeneoPlugin\Retriever\FamilyMeasureRetriever; use Synolia\SyliusAkeneoPlugin\TypeMatcher\Attribute\AttributeTypeMatcher; diff --git a/src/Command/AbstractImportCommand.php b/src/Command/AbstractImportCommand.php index a796c6af..cb53c322 100644 --- a/src/Command/AbstractImportCommand.php +++ b/src/Command/AbstractImportCommand.php @@ -43,6 +43,7 @@ protected function configure(): void ->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') + ->addOption('handler', 'i', InputOption::VALUE_OPTIONAL, 'Specify batch handler') ; } diff --git a/src/Configuration/ConfigurationContextInterface.php b/src/Configuration/ConfigurationContextInterface.php index cb428371..5e0a062d 100644 --- a/src/Configuration/ConfigurationContextInterface.php +++ b/src/Configuration/ConfigurationContextInterface.php @@ -39,4 +39,8 @@ public function disableBatching(): self; public function getFilters(): array; public function setFilters(array $filters): self; + + public function getHandler(): string; + + public function setHandler(string $handler): self; } diff --git a/src/Configuration/ConfigurationContextTrait.php b/src/Configuration/ConfigurationContextTrait.php index 2587617d..7672eefe 100644 --- a/src/Configuration/ConfigurationContextTrait.php +++ b/src/Configuration/ConfigurationContextTrait.php @@ -5,6 +5,7 @@ namespace Synolia\SyliusAkeneoPlugin\Configuration; use Symfony\Component\Console\Output\OutputInterface; +use Synolia\SyliusAkeneoPlugin\Handler\Task\SymfonyProcessTaskHandler; trait ConfigurationContextTrait { @@ -24,6 +25,8 @@ trait ConfigurationContextTrait private array $filters = []; + private string $handler = SymfonyProcessTaskHandler::HANDLER_CODE; + public function getBatchSize(): int { return $this->batchSize; @@ -132,4 +135,16 @@ public function setFilters(array $filters): ConfigurationContextInterface return $this; } + + public function getHandler(): string + { + return $this->handler; + } + + public function setHandler(string $handler): ConfigurationContextInterface + { + $this->handler = $handler; + + return $this; + } } diff --git a/src/Entity/Asset.php b/src/Entity/Asset.php index d0c340f5..81c1f93d 100644 --- a/src/Entity/Asset.php +++ b/src/Entity/Asset.php @@ -22,6 +22,7 @@ */ #[ORM\Entity(repositoryClass: AssetRepository::class)] #[ORM\Table(name: 'akeneo_assets')] +#[ORM\Index(columns: ['family_code', 'asset_code', 'attribute_code'], name: 'asset_idx')] class Asset implements AssetInterface { /** diff --git a/src/Exceptions/NotImplementedException.php b/src/Exceptions/NotImplementedException.php new file mode 100644 index 00000000..fdd48440 --- /dev/null +++ b/src/Exceptions/NotImplementedException.php @@ -0,0 +1,9 @@ +createBatchMessage($items); + } +} diff --git a/src/Factory/Message/Batch/BatchMessageFactoryInterface.php b/src/Factory/Message/Batch/BatchMessageFactoryInterface.php new file mode 100644 index 00000000..65586634 --- /dev/null +++ b/src/Factory/Message/Batch/BatchMessageFactoryInterface.php @@ -0,0 +1,13 @@ +setBatchSize((int) $input->getOption('batch-size')) ->setMaxRunningProcessQueueSize((int) $input->getOption('max-concurrency')) ->setFilters((array) ($input->getOption('filter') ?: [])) + ->setHandler($input->getOption('handler') ?? $context->getHandler()) ; if (!$isBatchingAllowed) { diff --git a/src/Factory/ProductPipelineFactory.php b/src/Factory/ProductPipelineFactory.php index 73289b1e..4561cbf9 100644 --- a/src/Factory/ProductPipelineFactory.php +++ b/src/Factory/ProductPipelineFactory.php @@ -8,8 +8,8 @@ use League\Pipeline\PipelineInterface; use Synolia\SyliusAkeneoPlugin\Pipeline\Processor; use Synolia\SyliusAkeneoPlugin\Task\Product\ProcessProductsTask; -use Synolia\SyliusAkeneoPlugin\Task\Product\SetupProductTask; -use Synolia\SyliusAkeneoPlugin\Task\Product\TearDownProductTask; +use Synolia\SyliusAkeneoPlugin\Task\SetupTask; +use Synolia\SyliusAkeneoPlugin\Task\TearDownTask; final class ProductPipelineFactory extends AbstractPipelineFactory { @@ -18,9 +18,9 @@ public function create(): PipelineInterface $pipeline = new Pipeline(new Processor($this->dispatcher)); return $pipeline - ->pipe($this->taskProvider->get(SetupProductTask::class)) + ->pipe($this->taskProvider->get(SetupTask::class)) ->pipe($this->taskProvider->get(ProcessProductsTask::class)) - ->pipe($this->taskProvider->get(TearDownProductTask::class)) + ->pipe($this->taskProvider->get(TearDownTask::class)) ; } } diff --git a/src/Handler/Task/SymfonyMessengerTaskHandler.php b/src/Handler/Task/SymfonyMessengerTaskHandler.php new file mode 100644 index 00000000..3546765d --- /dev/null +++ b/src/Handler/Task/SymfonyMessengerTaskHandler.php @@ -0,0 +1,124 @@ +getHandler() === self::HANDLER_CODE; + } + + public function batch(PipelinePayloadInterface $pipelinePayload, array $items): void + { + $this->bus->dispatch($this->batchMessageFactory->createFromPayload($pipelinePayload, $items)); + } + + public function handle( + PipelinePayloadInterface $pipelinePayload, + ResourceCursorInterface|PageInterface $handleType, + ): void { + $count = 0; + $items = []; + + if ($handleType instanceof PageInterface) { + $this->handleByPage($pipelinePayload, $handleType, $count, $items); + } else { + $this->handleByCursor($pipelinePayload, $handleType, $count, $items); + } + } + + private function handleByPage( + PipelinePayloadInterface $payload, + PageInterface $page, + int &$count = 0, + array &$items = [], + ): void { + while ( + ($page instanceof Page && $page->hasNextPage()) || + ($page instanceof Page && !$page->hasPreviousPage()) || + $page instanceof Page + ) { + foreach ($page->getItems() as $item) { + ++$count; + $items[] = $item; + $identifiers[] = $item['code'] ?? $item['identifier']; + + if (0 === $count % $payload->getBatchSize()) { + $this->logger->notice('Batching', ['codes' => $identifiers]); + $this->batch($payload, $items); + $items = []; + } + } + + $page = $page->getNextPage(); + } + + if ($items !== []) { + $this->batch($payload, $items); + $items = []; + } + } + + private function handleByCursor( + PipelinePayloadInterface $payload, + ResourceCursorInterface $resourceCursor, + int &$count = 0, + array &$items = [], + ): void { + /** + * @var array $item + */ + foreach ($resourceCursor as $item) { + ++$count; + $items[] = $item; + $identifiers[] = $item['code'] ?? $item['identifier']; + + if (0 === $count % $payload->getBatchSize()) { + $this->logger->notice('Batching', ['codes' => $identifiers]); + $this->batch($payload, $items); + $items = []; + } + } + + if ($items !== []) { + $this->batch($payload, $items); + $items = []; + } + } + + public function setUp(PipelinePayloadInterface $pipelinePayload): PipelinePayloadInterface + { + return $pipelinePayload; + } + + public function tearDown(PipelinePayloadInterface $pipelinePayload): PipelinePayloadInterface + { + return $pipelinePayload; + } + + public function continue(PipelinePayloadInterface $pipelinePayload): void + { + } +} diff --git a/src/Task/AbstractProcessTask.php b/src/Handler/Task/SymfonyProcessTaskHandler.php similarity index 53% rename from src/Task/AbstractProcessTask.php rename to src/Handler/Task/SymfonyProcessTaskHandler.php index 9685529c..c0065ad8 100644 --- a/src/Task/AbstractProcessTask.php +++ b/src/Handler/Task/SymfonyProcessTaskHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Synolia\SyliusAkeneoPlugin\Task; +namespace Synolia\SyliusAkeneoPlugin\Handler\Task; use Akeneo\Pim\ApiClient\Pagination\Page; use Akeneo\Pim\ApiClient\Pagination\PageInterface; @@ -13,28 +13,30 @@ use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Process\Process; -use Synolia\SyliusAkeneoPlugin\Logger\Messages; use Synolia\SyliusAkeneoPlugin\Manager\ProcessManagerInterface; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; -use Throwable; +use Synolia\SyliusAkeneoPlugin\Provider\Task\Batch\BatchTaskProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Task\Batch\Payload\PayloadBatchTaskProvider; -abstract class AbstractProcessTask implements AkeneoTaskInterface +class SymfonyProcessTaskHandler implements TaskHandlerInterface { - private int $updateCount = 0; - - private int $createCount = 0; - - private string $type; + public const HANDLER_CODE = 'process'; public function __construct( protected EntityManagerInterface $entityManager, protected ProcessManagerInterface $processManager, - protected BatchTaskInterface $task, protected LoggerInterface $logger, private string $projectDir, + private PayloadBatchTaskProvider $payloadBatchTaskProvider, + private BatchTaskProvider $batchTaskProvider, ) { } + public function support(PipelinePayloadInterface $pipelinePayload): bool + { + return $pipelinePayload->getHandler() === self::HANDLER_CODE; + } + protected function count(string $tableName): int { /** @var Result $query */ @@ -76,20 +78,20 @@ protected function prepareSelectBatchIdsQuery( return $query; } - protected function batch( - PipelinePayloadInterface $payload, + public function batch( + PipelinePayloadInterface $pipelinePayload, array $ids, ): void { - if ($payload->allowParallel()) { + if ($pipelinePayload->allowParallel()) { $processArguments = [ 'php', 'bin/console', - $payload->getCommandName(), + $pipelinePayload->getCommandName(), implode(',', $ids), ]; - if ('' !== $payload->getVerbosityArgument()) { - $processArguments[] = $payload->getVerbosityArgument(); + if ('' !== $pipelinePayload->getVerbosityArgument()) { + $processArguments[] = $pipelinePayload->getVerbosityArgument(); } $process = new Process($processArguments, $this->projectDir); @@ -105,54 +107,38 @@ protected function batch( return; } - $batchPayload = $this->createBatchPayload($payload); + $batchPayload = $this->payloadBatchTaskProvider->createBatchPayload($pipelinePayload); $batchPayload->setIds($ids); - $this->task->__invoke($batchPayload); + $this->batchTaskProvider->getTask($pipelinePayload)->__invoke($batchPayload); } - abstract protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface; - - protected function process(PipelinePayloadInterface $initialPayload): void + public function continue(PipelinePayloadInterface $pipelinePayload): void { - $this->processManager->setInstantProcessing($initialPayload->getProcessAsSoonAsPossible()); - $this->processManager->setNumberOfParallelProcesses($initialPayload->getMaxRunningProcessQueueSize()); - - $this->logger->debug(self::class); - $this->type = $initialPayload->getType(); - $this->logger->notice(Messages::createOrUpdate($this->type)); - - try { - $totalItemsCount = $this->count($initialPayload->getTmpTableName()); + $totalItemsCount = $this->count($pipelinePayload->getTmpTableName()); - if (0 === $totalItemsCount) { - return; - } - - $min = $this->min($initialPayload->getTmpTableName()); - $query = $this->prepareSelectBatchIdsQuery($initialPayload->getTmpTableName(), $min - 1, $initialPayload->getBatchSize()); - /** @var Result $queryResult */ - $queryResult = $query->executeQuery(); - - while ($results = $queryResult->fetchAllAssociative()) { - $ids = []; - foreach ($results as $result) { - $ids[] = $result['id']; - } + if (0 === $totalItemsCount) { + return; + } - $this->batch($initialPayload, $ids); + $min = $this->min($pipelinePayload->getTmpTableName()); + $query = $this->prepareSelectBatchIdsQuery($pipelinePayload->getTmpTableName(), $min - 1, $pipelinePayload->getBatchSize()); + /** @var Result $queryResult */ + $queryResult = $query->executeQuery(); - $query = $this->prepareSelectBatchIdsQuery($initialPayload->getTmpTableName(), (int) $result['id'], $initialPayload->getBatchSize()); - $queryResult = $query->executeQuery(); + while ($results = $queryResult->fetchAllAssociative()) { + $ids = []; + foreach ($results as $result) { + $ids[] = $result['id']; } - $this->processManager->startAll(); - } catch (Throwable $throwable) { - $this->logger->warning($throwable->getMessage()); + $this->batch($pipelinePayload, $ids); - throw $throwable; + $query = $this->prepareSelectBatchIdsQuery($pipelinePayload->getTmpTableName(), (int) $result['id'], $pipelinePayload->getBatchSize()); + $queryResult = $query->executeQuery(); } - $this->logger->notice(Messages::countCreateAndUpdate($this->type, $this->createCount, $this->updateCount)); + $this->processManager->startAll(); + $this->processManager->waitForAllProcesses(); } /** @@ -161,46 +147,51 @@ protected function process(PipelinePayloadInterface $initialPayload): void * * @TODO Probably need to be refactored */ - protected function handle( - PipelinePayloadInterface $payload, - \Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface|\Akeneo\Pim\ApiClient\Pagination\PageInterface $handleType, + public function handle( + PipelinePayloadInterface $pipelinePayload, + ResourceCursorInterface|PageInterface $handleType, ): void { - $this->processManager->setInstantProcessing($payload->getProcessAsSoonAsPossible()); - $this->processManager->setNumberOfParallelProcesses($payload->getMaxRunningProcessQueueSize()); + $this->processManager->setInstantProcessing($pipelinePayload->getProcessAsSoonAsPossible()); + $this->processManager->setNumberOfParallelProcesses($pipelinePayload->getMaxRunningProcessQueueSize()); $count = 0; $ids = []; if ($handleType instanceof PageInterface) { - $this->handleByPage($payload, $handleType, $count, $ids); + $this->handleByPage($pipelinePayload, $handleType, $count, $ids); } else { - $this->handleByCursor($payload, $handleType, $count, $ids); + $this->handleByCursor($pipelinePayload, $handleType, $count, $ids); } - if ($count > 0 && count($ids) > 0 && $payload->isBatchingAllowed() && $payload->getProcessAsSoonAsPossible() && $payload->allowParallel()) { + if ($count > 0 && count($ids) > 0 && $pipelinePayload->isBatchingAllowed() && $pipelinePayload->getProcessAsSoonAsPossible() && $pipelinePayload->allowParallel()) { $this->logger->notice('Batching', ['from_id' => $ids[0], 'to_id' => $ids[(is_countable($ids) ? \count($ids) : 0) - 1]]); - $this->batch($payload, $ids); + $this->batch($pipelinePayload, $ids); + $this->processManager->waitForAllProcesses(); return; } - if ($count > 0 && count($ids) > 0 && $payload->isBatchingAllowed() && $payload->getProcessAsSoonAsPossible() && !$payload->allowParallel()) { - $payload->setIds($ids); - $this->task->__invoke($payload); + if ($count > 0 && count($ids) > 0 && $pipelinePayload->isBatchingAllowed() && $pipelinePayload->getProcessAsSoonAsPossible() && !$pipelinePayload->allowParallel()) { + $pipelinePayload->setIds($ids); + $this->batchTaskProvider->getTask($pipelinePayload)->__invoke($pipelinePayload); + $this->processManager->waitForAllProcesses(); return; } - if ($count > 0 && count($ids) > 0 && !$payload->isBatchingAllowed()) { - $payload->setIds($ids); - $this->task->__invoke($payload); + if ($count > 0 && count($ids) > 0 && !$pipelinePayload->isBatchingAllowed()) { + $pipelinePayload->setIds($ids); + $this->batchTaskProvider->getTask($pipelinePayload)->__invoke($pipelinePayload); + $this->processManager->waitForAllProcesses(); return; } - if ($count > 0 && !$payload->getProcessAsSoonAsPossible()) { - $this->process($payload); + if ($count > 0 && !$pipelinePayload->getProcessAsSoonAsPossible()) { + $this->continue($pipelinePayload); } + + $this->processManager->waitForAllProcesses(); } private function handleByPage( @@ -266,4 +257,41 @@ private function handleByCursor( } } } + + public function setUp(PipelinePayloadInterface $pipelinePayload): PipelinePayloadInterface + { + if ($pipelinePayload->isContinue()) { + $schemaManager = $this->entityManager->getConnection()->createSchemaManager(); + $tableExist = $schemaManager->tablesExist([$pipelinePayload->getTmpTableName()]); + + if (true === $tableExist) { + return $pipelinePayload; + } + } + + $this->tearDown($pipelinePayload); + + $query = sprintf( + 'CREATE TABLE `%s` ( + `id` INT NOT NULL AUTO_INCREMENT, + `values` JSON NULL, + PRIMARY KEY (`id`));', + $pipelinePayload->getTmpTableName(), + ); + $this->entityManager->getConnection()->executeStatement($query); + + return $pipelinePayload; + } + + public function tearDown(PipelinePayloadInterface $pipelinePayload): PipelinePayloadInterface + { + $schemaManager = $this->entityManager->getConnection()->createSchemaManager(); + $exists = $schemaManager->tablesExist([$pipelinePayload->getTmpTableName()]); + + if ($exists) { + $schemaManager->dropTable($pipelinePayload->getTmpTableName()); + } + + return $pipelinePayload; + } } diff --git a/src/Handler/Task/TaskHandlerInterface.php b/src/Handler/Task/TaskHandlerInterface.php new file mode 100644 index 00000000..6dae779f --- /dev/null +++ b/src/Handler/Task/TaskHandlerInterface.php @@ -0,0 +1,32 @@ +items as $resource) { + try { + $this->resourceProcessor->process($resource); + } catch (MaxResourceProcessorRetryException) { + // Skip the failing line + $this->dispatcher->dispatch(new AssociationTypeBatchMessage([$resource])); + + continue; + } + } + } +} diff --git a/src/MessageHandler/Batch/AttributeBatchMessageHandler.php b/src/MessageHandler/Batch/AttributeBatchMessageHandler.php new file mode 100644 index 00000000..3568e62b --- /dev/null +++ b/src/MessageHandler/Batch/AttributeBatchMessageHandler.php @@ -0,0 +1,35 @@ +items as $resource) { + try { + $this->resourceProcessor->process($resource); + } catch (MaxResourceProcessorRetryException) { + // Skip the failing line + $this->dispatcher->dispatch(new AttributeBatchMessage([$resource])); + + continue; + } + } + } +} diff --git a/src/MessageHandler/Batch/ProductBatchMessageHandler.php b/src/MessageHandler/Batch/ProductBatchMessageHandler.php new file mode 100644 index 00000000..a68733b3 --- /dev/null +++ b/src/MessageHandler/Batch/ProductBatchMessageHandler.php @@ -0,0 +1,35 @@ +items as $resource) { + try { + $this->resourceProcessor->process($resource); + } catch (MaxResourceProcessorRetryException) { + // Skip the failing line + $this->dispatcher->dispatch(new ProductBatchMessage([$resource])); + + continue; + } + } + } +} diff --git a/src/MessageHandler/Batch/ProductVariantBatchMessageHandler.php b/src/MessageHandler/Batch/ProductVariantBatchMessageHandler.php new file mode 100644 index 00000000..81fe0c07 --- /dev/null +++ b/src/MessageHandler/Batch/ProductVariantBatchMessageHandler.php @@ -0,0 +1,35 @@ +items as $resource) { + try { + $this->resourceProcessor->process($resource); + } catch (MaxResourceProcessorRetryException) { + // Skip the failing line + $this->dispatcher->dispatch(new ProductVariantBatchMessage([$resource])); + + continue; + } + } + } +} diff --git a/src/Payload/AbstractPayload.php b/src/Payload/AbstractPayload.php index 6dca7945..b95a83d4 100644 --- a/src/Payload/AbstractPayload.php +++ b/src/Payload/AbstractPayload.php @@ -28,7 +28,7 @@ abstract class AbstractPayload implements PipelinePayloadInterface public function __construct( protected AkeneoPimClientInterface $akeneoPimClient, - protected ?\Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface $commandContext = null, + protected ?CommandContextInterface $commandContext = null, ) { if (null !== $commandContext) { $this->allowParallel = $commandContext->allowParallel(); @@ -38,6 +38,7 @@ public function __construct( $this->verbosity = $commandContext->getVerbosity(); $this->isContinue = $commandContext->isContinue(); $this->processAsSoonAsPossible = $commandContext->getProcessAsSoonAsPossible(); + $this->handler = $commandContext->getHandler(); } } diff --git a/src/Payload/Asset/AssetFamilyPayload.php b/src/Payload/Asset/AssetFamilyPayload.php index b4b1cd56..1108643d 100644 --- a/src/Payload/Asset/AssetFamilyPayload.php +++ b/src/Payload/Asset/AssetFamilyPayload.php @@ -7,6 +7,7 @@ use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface; use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class AssetFamilyPayload extends AbstractPayload @@ -36,4 +37,9 @@ public function setResources(ResourceCursorInterface $resources): void { $this->resources = $resources; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new \InvalidArgumentException(); + } } diff --git a/src/Payload/Asset/AssetPayload.php b/src/Payload/Asset/AssetPayload.php index 67689bf8..9449262a 100644 --- a/src/Payload/Asset/AssetPayload.php +++ b/src/Payload/Asset/AssetPayload.php @@ -7,6 +7,7 @@ use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface; use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class AssetPayload extends AbstractPayload @@ -36,4 +37,9 @@ public function setResources(ResourceCursorInterface $resources): void { $this->resources = $resources; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new \InvalidArgumentException(); + } } diff --git a/src/Payload/Association/AssociationPayload.php b/src/Payload/Association/AssociationPayload.php index a18a41d3..eeb2a683 100644 --- a/src/Payload/Association/AssociationPayload.php +++ b/src/Payload/Association/AssociationPayload.php @@ -4,8 +4,13 @@ namespace Synolia\SyliusAkeneoPlugin\Payload\Association; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class AssociationPayload extends AbstractPayload { + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new \InvalidArgumentException(); + } } diff --git a/src/Payload/Association/AssociationTypePayload.php b/src/Payload/Association/AssociationTypePayload.php index 86dea0c0..bf6fdba5 100644 --- a/src/Payload/Association/AssociationTypePayload.php +++ b/src/Payload/Association/AssociationTypePayload.php @@ -7,6 +7,8 @@ use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface; use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\AssociationTypeBatchMessage; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class AssociationTypePayload extends AbstractPayload @@ -36,4 +38,9 @@ public function setResources(ResourceCursorInterface $resources): void { $this->resources = $resources; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + return new AssociationTypeBatchMessage($items); + } } diff --git a/src/Payload/Attribute/AttributePayload.php b/src/Payload/Attribute/AttributePayload.php index eb35a16e..76ceba8c 100644 --- a/src/Payload/Attribute/AttributePayload.php +++ b/src/Payload/Attribute/AttributePayload.php @@ -7,6 +7,8 @@ use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface; use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\AttributeBatchMessage; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class AttributePayload extends AbstractPayload @@ -36,4 +38,9 @@ public function setResources(ResourceCursorInterface $resources): void { $this->resources = $resources; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + return new AttributeBatchMessage($items); + } } diff --git a/src/Payload/Attribute/LocaleAttributeTranslationPayload.php b/src/Payload/Attribute/LocaleAttributeTranslationPayload.php index 967609bd..f6332ae8 100644 --- a/src/Payload/Attribute/LocaleAttributeTranslationPayload.php +++ b/src/Payload/Attribute/LocaleAttributeTranslationPayload.php @@ -6,6 +6,7 @@ use Sylius\Component\Attribute\Model\AttributeInterface; use Sylius\Component\Core\Model\ProductInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class LocaleAttributeTranslationPayload extends AbstractPayload @@ -93,4 +94,9 @@ public function setScope(string $scope): self return $this; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new \InvalidArgumentException(); + } } diff --git a/src/Payload/Category/CategoryPayload.php b/src/Payload/Category/CategoryPayload.php index 951376cf..6f650940 100644 --- a/src/Payload/Category/CategoryPayload.php +++ b/src/Payload/Category/CategoryPayload.php @@ -5,6 +5,7 @@ namespace Synolia\SyliusAkeneoPlugin\Payload\Category; use Synolia\SyliusAkeneoPlugin\Exceptions\NoCategoryResourcesException; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class CategoryPayload extends AbstractPayload @@ -24,4 +25,9 @@ public function setResources(array $resources): void { $this->resources = $resources; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new \InvalidArgumentException(); + } } diff --git a/src/Payload/ConfigurationPayload.php b/src/Payload/ConfigurationPayload.php index 56712fbb..9b15bf2c 100644 --- a/src/Payload/ConfigurationPayload.php +++ b/src/Payload/ConfigurationPayload.php @@ -4,6 +4,13 @@ namespace Synolia\SyliusAkeneoPlugin\Payload; +use Synolia\SyliusAkeneoPlugin\Exceptions\NotImplementedException; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; + final class ConfigurationPayload extends AbstractPayload implements PipelinePayloadInterface { + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new NotImplementedException(); + } } diff --git a/src/Payload/Option/OptionsPayload.php b/src/Payload/Option/OptionsPayload.php deleted file mode 100644 index 3ee4b09c..00000000 --- a/src/Payload/Option/OptionsPayload.php +++ /dev/null @@ -1,40 +0,0 @@ - */ - private array $selectOptionsResources; - - /** @var array<\Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface> */ - private array $referenceEntityOptionsResources; - - public function getSelectOptionsResources(): array - { - return $this->selectOptionsResources; - } - - public function setSelectOptionsResources(array $selectOptionsResources): self - { - $this->selectOptionsResources = $selectOptionsResources; - - return $this; - } - - public function getReferenceEntityOptionsResources(): array - { - return $this->referenceEntityOptionsResources; - } - - public function setReferenceEntityOptionsResources(array $referenceEntityOptionsResources): self - { - $this->referenceEntityOptionsResources = $referenceEntityOptionsResources; - - return $this; - } -} diff --git a/src/Payload/PipelinePayloadInterface.php b/src/Payload/PipelinePayloadInterface.php index 805ad44b..9a65513b 100644 --- a/src/Payload/PipelinePayloadInterface.php +++ b/src/Payload/PipelinePayloadInterface.php @@ -5,10 +5,13 @@ namespace Synolia\SyliusAkeneoPlugin\Payload; use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; interface PipelinePayloadInterface extends PayloadInterface { public function getAkeneoPimClient(): AkeneoPimClientInterface; public function getType(): string; + + public function createBatchMessage(array $items): BatchMessageInterface; } diff --git a/src/Payload/Product/ProductCategoriesPayload.php b/src/Payload/Product/ProductCategoriesPayload.php index 696827ba..835a3f41 100644 --- a/src/Payload/Product/ProductCategoriesPayload.php +++ b/src/Payload/Product/ProductCategoriesPayload.php @@ -5,6 +5,7 @@ namespace Synolia\SyliusAkeneoPlugin\Payload\Product; use Sylius\Component\Core\Model\ProductInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class ProductCategoriesPayload extends AbstractPayload @@ -36,4 +37,9 @@ public function setCategories(array $categories): self return $this; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new \InvalidArgumentException(); + } } diff --git a/src/Payload/Product/ProductMediaPayload.php b/src/Payload/Product/ProductMediaPayload.php index 3ffd606a..c601e975 100644 --- a/src/Payload/Product/ProductMediaPayload.php +++ b/src/Payload/Product/ProductMediaPayload.php @@ -6,6 +6,7 @@ use Sylius\Component\Core\Model\ProductInterface; use Synolia\SyliusAkeneoPlugin\Entity\ProductConfiguration; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class ProductMediaPayload extends AbstractPayload implements ProductMediaPayloadInterface @@ -49,4 +50,9 @@ public function setProductConfiguration(ProductConfiguration $productConfigurati { $this->productConfiguration = $productConfiguration; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new \InvalidArgumentException(); + } } diff --git a/src/Payload/Product/ProductPayload.php b/src/Payload/Product/ProductPayload.php index 6f3df5c1..c63e68f1 100644 --- a/src/Payload/Product/ProductPayload.php +++ b/src/Payload/Product/ProductPayload.php @@ -7,6 +7,8 @@ use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; use Akeneo\Pim\ApiClient\Pagination\PageInterface; use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\ProductVariantBatchMessage; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class ProductPayload extends AbstractPayload @@ -36,4 +38,9 @@ public function setResources(PageInterface $resources): void { $this->resources = $resources; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + return new ProductVariantBatchMessage($items); + } } diff --git a/src/Payload/Product/ProductResourcePayload.php b/src/Payload/Product/ProductResourcePayload.php index 6663cd64..2c645883 100644 --- a/src/Payload/Product/ProductResourcePayload.php +++ b/src/Payload/Product/ProductResourcePayload.php @@ -5,6 +5,7 @@ namespace Synolia\SyliusAkeneoPlugin\Payload\Product; use Sylius\Component\Core\Model\ProductInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class ProductResourcePayload extends AbstractPayload @@ -74,4 +75,9 @@ public function setScope(string $scope): self return $this; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new \InvalidArgumentException(); + } } diff --git a/src/Payload/Product/ProductVariantMediaPayload.php b/src/Payload/Product/ProductVariantMediaPayload.php index 2b8772d7..68ee90df 100644 --- a/src/Payload/Product/ProductVariantMediaPayload.php +++ b/src/Payload/Product/ProductVariantMediaPayload.php @@ -5,6 +5,7 @@ namespace Synolia\SyliusAkeneoPlugin\Payload\Product; use Sylius\Component\Core\Model\ProductVariantInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class ProductVariantMediaPayload extends AbstractPayload implements ProductMediaPayloadInterface @@ -36,4 +37,9 @@ public function setAttributes(array $attributes): self return $this; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new \InvalidArgumentException(); + } } diff --git a/src/Payload/ProductModel/ProductModelPayload.php b/src/Payload/ProductModel/ProductModelPayload.php index 6f6c2575..fd8ce7d1 100644 --- a/src/Payload/ProductModel/ProductModelPayload.php +++ b/src/Payload/ProductModel/ProductModelPayload.php @@ -7,6 +7,8 @@ use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface; use Synolia\SyliusAkeneoPlugin\Command\Context\CommandContextInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; +use Synolia\SyliusAkeneoPlugin\Message\Batch\ProductBatchMessage; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class ProductModelPayload extends AbstractPayload @@ -36,4 +38,9 @@ public function setResources(ResourceCursorInterface $resources): void { $this->resources = $resources; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + return new ProductBatchMessage($items); + } } diff --git a/src/Payload/ReferenceEntity/ReferenceEntityOptionsPayload.php b/src/Payload/ReferenceEntity/ReferenceEntityOptionsPayload.php index ecd9abc9..cbef215f 100644 --- a/src/Payload/ReferenceEntity/ReferenceEntityOptionsPayload.php +++ b/src/Payload/ReferenceEntity/ReferenceEntityOptionsPayload.php @@ -4,6 +4,7 @@ namespace Synolia\SyliusAkeneoPlugin\Payload\ReferenceEntity; +use Synolia\SyliusAkeneoPlugin\Message\Batch\BatchMessageInterface; use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; final class ReferenceEntityOptionsPayload extends AbstractPayload @@ -24,4 +25,9 @@ public function setResource(string $attributeCode, array $resources): void { $this->resources[$attributeCode] = $resources; } + + public function createBatchMessage(array $items): BatchMessageInterface + { + throw new \InvalidArgumentException(); + } } diff --git a/src/Processor/Product/CompleteRequirementProcessor.php b/src/Processor/Product/CompleteRequirementProcessor.php index d510d765..d5f573bb 100644 --- a/src/Processor/Product/CompleteRequirementProcessor.php +++ b/src/Processor/Product/CompleteRequirementProcessor.php @@ -16,8 +16,8 @@ use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\AkeneoFamilyPropertiesProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\ProductFilterRulesProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider; diff --git a/src/Processor/ProductAttribute/AbstractModelAkeneoAttributeProcessor.php b/src/Processor/ProductAttribute/AbstractModelAkeneoAttributeProcessor.php index e3f587af..8dbe4ad1 100644 --- a/src/Processor/ProductAttribute/AbstractModelAkeneoAttributeProcessor.php +++ b/src/Processor/ProductAttribute/AbstractModelAkeneoAttributeProcessor.php @@ -7,8 +7,8 @@ use Psr\Log\LoggerInterface; use Sylius\Component\Resource\Model\ResourceInterface; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider; abstract class AbstractModelAkeneoAttributeProcessor diff --git a/src/Processor/ProductAttribute/AssetAttributeProcessor.php b/src/Processor/ProductAttribute/AssetAttributeProcessor.php index d2949c28..c4aa1083 100644 --- a/src/Processor/ProductAttribute/AssetAttributeProcessor.php +++ b/src/Processor/ProductAttribute/AssetAttributeProcessor.php @@ -18,7 +18,7 @@ use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException; use Synolia\SyliusAkeneoPlugin\Exceptions\UnsupportedAttributeTypeException; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider; use Synolia\SyliusAkeneoPlugin\Transformer\AkeneoAttributeToSyliusAttributeTransformerInterface; use Webmozart\Assert\Assert; diff --git a/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php b/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php index 34a73920..e98316d1 100644 --- a/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php +++ b/src/Processor/ProductAttribute/ProductAttributeAkeneoAttributeProcessor.php @@ -16,7 +16,7 @@ use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface; use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider; use Synolia\SyliusAkeneoPlugin\Transformer\AkeneoAttributeToSyliusAttributeTransformerInterface; diff --git a/src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php b/src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php index ecbc5eb0..7e26bd2a 100644 --- a/src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php +++ b/src/Processor/ProductOptionValue/ReferenceEntityOptionValuesProcessor.php @@ -15,7 +15,7 @@ use Sylius\Component\Resource\Repository\RepositoryInterface; use Synolia\SyliusAkeneoPlugin\Checker\EditionCheckerInterface; use Synolia\SyliusAkeneoPlugin\Component\Attribute\AttributeType\ReferenceEntityAttributeType; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Repository\LocaleRepositoryInterface; use Synolia\SyliusAkeneoPlugin\Transformer\ProductOptionValueDataTransformerInterface; use Webmozart\Assert\Assert; diff --git a/src/Processor/Resource/AkeneoResourceProcessorInterface.php b/src/Processor/Resource/AkeneoResourceProcessorInterface.php new file mode 100644 index 00000000..0e80bec3 --- /dev/null +++ b/src/Processor/Resource/AkeneoResourceProcessorInterface.php @@ -0,0 +1,10 @@ +retryCount === $this->maxRetryCount) { + throw new MaxResourceProcessorRetryException(); + } + + try { + $this->akeneoLogger->notice('Association Type', [ + 'code' => $resource['code'] ?? 'unknown', + ]); + + $productAssociationType = $this->productAssociationTypeRepository->findOneBy(['code' => $resource['code']]); + if (!$productAssociationType instanceof ProductAssociationTypeInterface) { + /** @var ProductAssociationTypeInterface $productAssociationType */ + $productAssociationType = $this->productAssociationTypeFactory->createNew(); + $this->entityManager->persist($productAssociationType); + + $productAssociationType->setCode($resource['code']); + } + + $this->setTranslations($resource['labels'], $productAssociationType); + + $this->entityManager->flush(); + } 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 setTranslations(array $labels, ProductAssociationTypeInterface $productAssociationType): void + { + foreach ($this->syliusAkeneoLocaleCodeProvider->getUsedLocalesOnBothPlatforms() as $usedLocalesOnBothPlatform) { + $akeneoLocale = $this->syliusAkeneoLocaleCodeProvider->getAkeneoLocale($usedLocalesOnBothPlatform); + + $productAssociationType->setCurrentLocale($usedLocalesOnBothPlatform); + $productAssociationType->setFallbackLocale($usedLocalesOnBothPlatform); + + if (!isset($labels[$akeneoLocale])) { + $productAssociationType->setName(sprintf('[%s]', $productAssociationType->getCode())); + + continue; + } + + $productAssociationType->setName($labels[$akeneoLocale]); + } + } + + private function getNewEntityManager(): EntityManagerInterface + { + $objectManager = $this->managerRegistry->resetManager(); + + if (!$objectManager instanceof EntityManagerInterface) { + throw new \LogicException('Wrong ObjectManager'); + } + + return $objectManager; + } +} diff --git a/src/Processor/Resource/Attribute/AttributeResourceProcessor.php b/src/Processor/Resource/Attribute/AttributeResourceProcessor.php new file mode 100644 index 00000000..29bb6fa8 --- /dev/null +++ b/src/Processor/Resource/Attribute/AttributeResourceProcessor.php @@ -0,0 +1,149 @@ +retryCount === $this->maxRetryCount) { + throw new MaxResourceProcessorRetryException(); + } + + $variationAxes = array_unique($this->getVariationAxes()); + + try { + $this->akeneoLogger->notice('Processing attribute', [ + 'code' => $resource['code'] ?? 'unknown', + ]); + + $this->dispatcher->dispatch(new BeforeProcessingAttributeEvent($resource)); + + if (!$this->entityManager->getConnection()->isTransactionActive()) { + $this->entityManager->beginTransaction(); + } + + $attribute = $this->attributeCreator->create($resource); + $this->entityManager->flush(); + + //Handle attribute options + $this->attributeChoiceProcessor->process($attribute, $resource); + + //Handle attribute table configuration + $this->productAttributeTableProcessor->process($attribute, $resource); + + //Handler options + $this->productOptionProcessor->process($attribute, $variationAxes); + + $this->dispatcher->dispatch(new AfterProcessingAttributeEvent($resource, $attribute)); + + $this->entityManager->flush(); + } 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 getVariationAxes(): array + { + $variationAxes = []; + $families = $this->familyRetriever->getFamilies(); + + foreach ($families as $family) { + $familyVariants = $this->familyVariantRetriever->getVariants($family['code']); + + $variationAxes = array_merge($variationAxes, $this->getVariationAxesForFamilies($familyVariants)); + } + + return $variationAxes; + } + + private function getVariationAxesForFamilies(array $familyVariants): array + { + $variationAxes = []; + + /** @var array{variant_attribute_sets: array} $familyVariant */ + foreach ($familyVariants as $familyVariant) { + /** @var array{axes: array} $variantAttributeSet */ + foreach ($familyVariant['variant_attribute_sets'] as $variantAttributeSet) { + foreach ($variantAttributeSet['axes'] as $axe) { + $variationAxes[] = $axe; + } + } + } + + return $variationAxes; + } + + private function getNewEntityManager(): EntityManagerInterface + { + $objectManager = $this->managerRegistry->resetManager(); + + if (!$objectManager instanceof EntityManagerInterface) { + throw new \LogicException('Wrong ObjectManager'); + } + + return $objectManager; + } +} diff --git a/src/Processor/Resource/Exception/MaxResourceProcessorRetryException.php b/src/Processor/Resource/Exception/MaxResourceProcessorRetryException.php new file mode 100644 index 00000000..1db096b2 --- /dev/null +++ b/src/Processor/Resource/Exception/MaxResourceProcessorRetryException.php @@ -0,0 +1,9 @@ +retryCount === $this->maxRetryCount) { + throw new MaxResourceProcessorRetryException(); + } + + try { + $this->akeneoLogger->notice('Processing product', [ + 'code' => $resource['code'] ?? $resource['identifier'] ?? 'unknown', + ]); + + $this->handleProductGroup($resource); + $this->dispatcher->dispatch(new BeforeProcessingProductEvent($resource)); + + if (!$this->isProductProcessableChecker->check($resource)) { + return; + } + + $product = $this->getOrCreateEntity($resource); + $this->productProcessorChain->chain($product, $resource); + + // TODO: check if id is null + $this->akeneoLogger->info(Messages::hasBeenCreated($product::class, (string) $product->getCode())); + $this->akeneoLogger->info(Messages::hasBeenUpdated($product::class, (string) $resource['code'])); + + $this->dispatcher->dispatch(new AfterProcessingProductEvent($resource, $product)); + $this->entityManager->flush(); + } 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 handleProductGroup(array $resource): void + { + try { + $this->productGroupProcessor->process($resource); + $this->entityManager->flush(); + } catch (ORMInvalidArgumentException $ormInvalidArgumentException) { + if (!$this->entityManager->isOpen()) { + $this->akeneoLogger->warning('Recreating entity manager', ['exception' => $ormInvalidArgumentException]); + $this->entityManager = $this->getNewEntityManager(); + } + + ++$this->retryCount; + + throw $ormInvalidArgumentException; + } + } + + private function getOrCreateEntity(array &$resource): ProductInterface + { + $product = $this->productRepository->findOneByCode($resource['code']); + + if (!$product instanceof ProductInterface) { + /** @var ProductInterface $product */ + $product = $this->productFactory->createNew(); + $product->setCode($resource['code']); + + $this->entityManager->persist($product); + + return $product; + } + + return $product; + } + + private function getNewEntityManager(): EntityManagerInterface + { + $objectManager = $this->managerRegistry->resetManager(); + + if (!$objectManager instanceof EntityManagerInterface) { + throw new \LogicException('Wrong ObjectManager'); + } + + return $objectManager; + } +} diff --git a/src/Task/Product/ConfigurableProductsTask.php b/src/Processor/Resource/ProductVariant/ConfigurableProductVariantResourceProcessor.php similarity index 65% rename from src/Task/Product/ConfigurableProductsTask.php rename to src/Processor/Resource/ProductVariant/ConfigurableProductVariantResourceProcessor.php index e58dfc4a..b07d2f1b 100644 --- a/src/Task/Product/ConfigurableProductsTask.php +++ b/src/Processor/Resource/ProductVariant/ConfigurableProductVariantResourceProcessor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Synolia\SyliusAkeneoPlugin\Task\Product; +namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\ProductVariant; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; @@ -11,68 +11,73 @@ use Sylius\Component\Product\Factory\ProductVariantFactoryInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Synolia\SyliusAkeneoPlugin\Client\ClientFactoryInterface; use Synolia\SyliusAkeneoPlugin\Config\AkeneoAxesEnum; use Synolia\SyliusAkeneoPlugin\Entity\ProductGroup; use Synolia\SyliusAkeneoPlugin\Entity\ProductGroupInterface; use Synolia\SyliusAkeneoPlugin\Event\ProductVariant\AfterProcessingProductVariantEvent; use Synolia\SyliusAkeneoPlugin\Event\ProductVariant\BeforeProcessingProductVariantEvent; -use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; -use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload; -use Synolia\SyliusAkeneoPlugin\Processor\Product\ProductChannelEnablerProcessorInterface; use Synolia\SyliusAkeneoPlugin\Processor\ProductVariant\ProductVariantProcessorChainInterface; +use Synolia\SyliusAkeneoPlugin\Processor\Resource\AkeneoResourceProcessorInterface; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface; -use Synolia\SyliusAkeneoPlugin\Repository\ChannelRepository; -use Synolia\SyliusAkeneoPlugin\Repository\LocaleRepositoryInterface; use Synolia\SyliusAkeneoPlugin\Repository\ProductGroupRepository; +use Synolia\SyliusAkeneoPlugin\Retriever\FamilyVariantRetrieverInterface; use Throwable; -final class ConfigurableProductsTask extends AbstractCreateProductEntities +class ConfigurableProductVariantResourceProcessor implements AkeneoResourceProcessorInterface, ProductVariantAkeneoResourceProcessorInterface { - /** - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ public function __construct( - EntityManagerInterface $entityManager, - RepositoryInterface $productVariantRepository, - RepositoryInterface $productRepository, - ChannelRepository $channelRepository, - LocaleRepositoryInterface $localeRepository, - RepositoryInterface $productConfigurationRepository, + private EntityManagerInterface $entityManager, + private RepositoryInterface $productVariantRepository, + private RepositoryInterface $productRepository, private ProductGroupRepository $productGroupRepository, - ProductVariantFactoryInterface $productVariantFactory, - LoggerInterface $akeneoLogger, + private ProductVariantFactoryInterface $productVariantFactory, + private LoggerInterface $akeneoLogger, private EventDispatcherInterface $dispatcher, - ProductChannelEnablerProcessorInterface $productChannelEnabler, private ProductVariantProcessorChainInterface $productVariantProcessorChain, - private ClientFactoryInterface $clientFactory, private ApiConnectionProviderInterface $apiConnectionProvider, + private FamilyVariantRetrieverInterface $familyVariantRetriever, ) { - parent::__construct( - $entityManager, - $productVariantRepository, - $productRepository, - $channelRepository, - $localeRepository, - $productConfigurationRepository, - $productVariantFactory, - $akeneoLogger, - $productChannelEnabler, - ); } - /** - * @param ProductPayload $payload - * - * @inheritDoc - */ - public function __invoke(PipelinePayloadInterface $payload, array $resource): void + private function getModelCode(ProductGroupInterface $productGroup): string + { + if ($this->apiConnectionProvider->get()->getAxeAsModel() === AkeneoAxesEnum::COMMON && + $productGroup->getParent() !== null) { + return $productGroup->getParent()->getModel(); + } + + return $productGroup->getModel(); + } + + private function getOrCreateEntity(string $variantCode, ProductInterface $productModel): ProductVariantInterface + { + $productVariant = $this->productVariantRepository->findOneBy(['code' => $variantCode]); + + if (!$productVariant instanceof ProductVariantInterface) { + /** @var ProductVariantInterface $productVariant */ + $productVariant = $this->productVariantFactory->createForProduct($productModel); + $productVariant->setCode($variantCode); + + $this->entityManager->persist($productVariant); + + return $productVariant; + } + + return $productVariant; + } + + public function support(array $resource): bool + { + return null !== $resource['parent']; + } + + public function process(array $resource): void { try { $productGroup = $this->productGroupRepository->findOneBy(['model' => $resource['parent']]); if (!$productGroup instanceof ProductGroup) { - $this->logger->warning(sprintf( + $this->akeneoLogger->warning(sprintf( 'Skipped product "%s" because model "%s" does not exist as group.', $resource['identifier'], $resource['parent'], @@ -81,7 +86,7 @@ public function __invoke(PipelinePayloadInterface $payload, array $resource): vo return; } - $this->logger->info(sprintf( + $this->akeneoLogger->info(sprintf( 'Processing product "%s" on model "%s".', $resource['identifier'], $resource['parent'], @@ -92,7 +97,7 @@ public function __invoke(PipelinePayloadInterface $payload, array $resource): vo //Skip product variant import if it does not have a parent model on Sylius if (!$productModel instanceof ProductInterface || !\is_string($productModel->getCode())) { - $this->logger->warning(sprintf( + $this->akeneoLogger->warning(sprintf( 'Skipped product "%s" because model "%s" does not exist.', $resource['identifier'], $resource['parent'], @@ -103,17 +108,10 @@ public function __invoke(PipelinePayloadInterface $payload, array $resource): vo $this->dispatcher->dispatch(new BeforeProcessingProductVariantEvent($resource, $productModel)); - $familyVariantPayload = $this->clientFactory - ->createFromApiCredentials() - ->getFamilyVariantApi() - ->get( - $resource['family'], - $productGroup->getFamilyVariant(), - ) - ; + $familyVariantPayload = $this->familyVariantRetriever->getVariant($resource['family'], $productGroup->getFamilyVariant()); if (0 === (is_countable($familyVariantPayload['variant_attribute_sets']) ? \count($familyVariantPayload['variant_attribute_sets']) : 0)) { - $this->logger->warning(sprintf( + $this->akeneoLogger->warning(sprintf( 'Skipped product "%s" because group has no variation axis.', $resource['identifier'], )); @@ -125,38 +123,10 @@ public function __invoke(PipelinePayloadInterface $payload, array $resource): vo $this->productVariantProcessorChain->chain($productVariant, $resource); $this->dispatcher->dispatch(new AfterProcessingProductVariantEvent($resource, $productVariant)); - $this->entityManager->flush(); } catch (Throwable $throwable) { - $this->logger->warning($throwable->getMessage(), [ + $this->akeneoLogger->warning($throwable->getMessage(), [ 'exception' => $throwable, ]); } } - - private function getModelCode(ProductGroupInterface $productGroup): string - { - if ($this->apiConnectionProvider->get()->getAxeAsModel() === AkeneoAxesEnum::COMMON && - $productGroup->getParent() !== null) { - return $productGroup->getParent()->getModel(); - } - - return $productGroup->getModel(); - } - - private function getOrCreateEntity(string $variantCode, ProductInterface $productModel): ProductVariantInterface - { - $productVariant = $this->productVariantRepository->findOneBy(['code' => $variantCode]); - - if (!$productVariant instanceof ProductVariantInterface) { - /** @var ProductVariantInterface $productVariant */ - $productVariant = $this->productVariantFactory->createForProduct($productModel); - $productVariant->setCode($variantCode); - - $this->entityManager->persist($productVariant); - - return $productVariant; - } - - return $productVariant; - } } diff --git a/src/Processor/Resource/ProductVariant/ProductVariantAkeneoResourceProcessorInterface.php b/src/Processor/Resource/ProductVariant/ProductVariantAkeneoResourceProcessorInterface.php new file mode 100644 index 00000000..d412cfc7 --- /dev/null +++ b/src/Processor/Resource/ProductVariant/ProductVariantAkeneoResourceProcessorInterface.php @@ -0,0 +1,15 @@ +retryCount === $this->maxRetryCount) { + throw new MaxResourceProcessorRetryException(); + } + + try { + $this->akeneoLogger->notice('Processing product variant', [ + 'code' => $resource['code'] ?? $resource['identifier'] ?? 'unknown', + ]); + + foreach ($this->productVariantAkeneoResourceProcessors as $productVariantAkeneoResourceProcessor) { + if ($productVariantAkeneoResourceProcessor->support($resource)) { + $productVariantAkeneoResourceProcessor->process($resource); + } + } + + $this->entityManager->flush(); + } 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 getNewEntityManager(): EntityManagerInterface + { + $objectManager = $this->managerRegistry->resetManager(); + + if (!$objectManager instanceof EntityManagerInterface) { + throw new \LogicException('Wrong ObjectManager'); + } + + return $objectManager; + } +} diff --git a/src/Task/Product/SimpleProductTask.php b/src/Processor/Resource/ProductVariant/SimpleProductVariantResourceProcessor.php similarity index 63% rename from src/Task/Product/SimpleProductTask.php rename to src/Processor/Resource/ProductVariant/SimpleProductVariantResourceProcessor.php index bf02634c..ec72df16 100644 --- a/src/Task/Product/SimpleProductTask.php +++ b/src/Processor/Resource/ProductVariant/SimpleProductVariantResourceProcessor.php @@ -2,12 +2,13 @@ declare(strict_types=1); -namespace Synolia\SyliusAkeneoPlugin\Task\Product; +namespace Synolia\SyliusAkeneoPlugin\Processor\Resource\ProductVariant; use Doctrine\ORM\EntityManagerInterface; use LogicException; use Psr\Log\LoggerInterface; use Sylius\Component\Core\Model\ProductInterface; +use Sylius\Component\Core\Model\ProductVariantInterface; use Sylius\Component\Product\Factory\ProductFactoryInterface; use Sylius\Component\Product\Factory\ProductVariantFactoryInterface; use Sylius\Component\Resource\Factory\FactoryInterface; @@ -17,74 +18,41 @@ use Synolia\SyliusAkeneoPlugin\Event\Product\BeforeProcessingProductEvent; use Synolia\SyliusAkeneoPlugin\Event\ProductVariant\AfterProcessingProductVariantEvent; use Synolia\SyliusAkeneoPlugin\Event\ProductVariant\BeforeProcessingProductVariantEvent; -use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; -use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload; -use Synolia\SyliusAkeneoPlugin\Processor\Product\ProductChannelEnablerProcessorInterface; use Synolia\SyliusAkeneoPlugin\Processor\Product\ProductProcessorChainInterface; use Synolia\SyliusAkeneoPlugin\Processor\ProductVariant\ProductVariantProcessorChainInterface; -use Synolia\SyliusAkeneoPlugin\Repository\ChannelRepository; -use Synolia\SyliusAkeneoPlugin\Repository\LocaleRepositoryInterface; +use Synolia\SyliusAkeneoPlugin\Processor\Resource\AkeneoResourceProcessorInterface; use Throwable; -final class SimpleProductTask extends AbstractCreateProductEntities +class SimpleProductVariantResourceProcessor implements AkeneoResourceProcessorInterface, ProductVariantAkeneoResourceProcessorInterface { - /** - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ public function __construct( - RepositoryInterface $productRepository, - ChannelRepository $channelRepository, - RepositoryInterface $productVariantRepository, - LocaleRepositoryInterface $localeRepository, - RepositoryInterface $productConfigurationRepository, + private RepositoryInterface $productRepository, + private RepositoryInterface $productVariantRepository, private FactoryInterface $productFactory, - ProductVariantFactoryInterface $productVariantFactory, - EntityManagerInterface $entityManager, - LoggerInterface $akeneoLogger, + private ProductVariantFactoryInterface $productVariantFactory, + private EntityManagerInterface $entityManager, + private LoggerInterface $akeneoLogger, private EventDispatcherInterface $dispatcher, - ProductChannelEnablerProcessorInterface $productChannelEnabler, private ProductProcessorChainInterface $productProcessorChain, private ProductVariantProcessorChainInterface $productVariantProcessorChain, + protected RepositoryInterface $channelPricingRepository, + protected FactoryInterface $channelPricingFactory, ) { - parent::__construct( - $entityManager, - $productVariantRepository, - $productRepository, - $channelRepository, - $localeRepository, - $productConfigurationRepository, - $productVariantFactory, - $akeneoLogger, - $productChannelEnabler, - ); } - /** - * @param ProductPayload $payload - * - * @inheritDoc - */ - public function __invoke(PipelinePayloadInterface $payload, array $resource): void + private function getOrCreateSimpleVariant(ProductInterface $product): ProductVariantInterface { - try { - $this->dispatcher->dispatch(new BeforeProcessingProductEvent($resource)); - - $product = $this->getOrCreateEntity($resource); - $this->productProcessorChain->chain($product, $resource); + /** @var ProductVariantInterface $productVariant */ + $productVariant = $this->productVariantRepository->findOneBy(['code' => $product->getCode()]); - $this->dispatcher->dispatch(new AfterProcessingProductEvent($resource, $product)); - - $this->dispatcher->dispatch(new BeforeProcessingProductVariantEvent($resource, $product)); + if (!$productVariant instanceof ProductVariantInterface) { + $productVariant = $this->productVariantFactory->createForProduct($product); + $productVariant->setCode($product->getCode()); - $productVariant = $this->getOrCreateSimpleVariant($product); - $this->productVariantProcessorChain->chain($productVariant, $resource); - - $this->dispatcher->dispatch(new AfterProcessingProductVariantEvent($resource, $productVariant)); - - $this->entityManager->flush(); - } catch (Throwable $throwable) { - $this->logger->warning($throwable->getMessage(), ['exception' => $throwable]); + $this->entityManager->persist($productVariant); } + + return $productVariant; } private function getOrCreateEntity(array $resource): ProductInterface @@ -110,4 +78,30 @@ private function getOrCreateEntity(array $resource): ProductInterface return $product; } + + public function support(array $resource): bool + { + return null === $resource['parent']; + } + + public function process(array $resource): void + { + try { + $this->dispatcher->dispatch(new BeforeProcessingProductEvent($resource)); + + $product = $this->getOrCreateEntity($resource); + $this->productProcessorChain->chain($product, $resource); + + $this->dispatcher->dispatch(new AfterProcessingProductEvent($resource, $product)); + + $this->dispatcher->dispatch(new BeforeProcessingProductVariantEvent($resource, $product)); + + $productVariant = $this->getOrCreateSimpleVariant($product); + $this->productVariantProcessorChain->chain($productVariant, $resource); + + $this->dispatcher->dispatch(new AfterProcessingProductVariantEvent($resource, $productVariant)); + } catch (Throwable $throwable) { + $this->akeneoLogger->warning($throwable->getMessage(), ['exception' => $throwable]); + } + } } diff --git a/src/Provider/ChainOptionValuesDataProvider.php b/src/Provider/ChainOptionValuesProcessorProvider.php similarity index 88% rename from src/Provider/ChainOptionValuesDataProvider.php rename to src/Provider/ChainOptionValuesProcessorProvider.php index afc9b814..6ba4caac 100644 --- a/src/Provider/ChainOptionValuesDataProvider.php +++ b/src/Provider/ChainOptionValuesProcessorProvider.php @@ -10,7 +10,7 @@ use Synolia\SyliusAkeneoPlugin\Processor\ProductOptionValue\OptionValuesProcessorInterface; use Traversable; -final class ChainOptionValuesDataProvider implements OptionValuesProcessorProviderInterface +final class ChainOptionValuesProcessorProvider implements OptionValuesProcessorProviderInterface { /** @var array */ private array $optionValuesProcessors; @@ -20,6 +20,9 @@ public function __construct(Traversable $handlers) $this->optionValuesProcessors = iterator_to_array($handlers); } + /** + * @throws MissingProductOptionValuesProcessorException + */ public function getProcessor( AttributeInterface $attribute, ProductOptionInterface $productOption, diff --git a/src/Provider/AkeneoAttributeDataProvider.php b/src/Provider/Data/AkeneoAttributeDataProvider.php similarity index 96% rename from src/Provider/AkeneoAttributeDataProvider.php rename to src/Provider/Data/AkeneoAttributeDataProvider.php index 3c026e34..0cbee480 100644 --- a/src/Provider/AkeneoAttributeDataProvider.php +++ b/src/Provider/Data/AkeneoAttributeDataProvider.php @@ -2,13 +2,14 @@ declare(strict_types=1); -namespace Synolia\SyliusAkeneoPlugin\Provider; +namespace Synolia\SyliusAkeneoPlugin\Provider\Data; use Synolia\SyliusAkeneoPlugin\Builder\Attribute\ProductAttributeValueValueBuilder; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException; +use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider; final class AkeneoAttributeDataProvider implements AkeneoAttributeDataProviderInterface { diff --git a/src/Provider/AkeneoAttributeDataProviderInterface.php b/src/Provider/Data/AkeneoAttributeDataProviderInterface.php similarity index 93% rename from src/Provider/AkeneoAttributeDataProviderInterface.php rename to src/Provider/Data/AkeneoAttributeDataProviderInterface.php index 0c7a03b8..70548e94 100644 --- a/src/Provider/AkeneoAttributeDataProviderInterface.php +++ b/src/Provider/Data/AkeneoAttributeDataProviderInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Synolia\SyliusAkeneoPlugin\Provider; +namespace Synolia\SyliusAkeneoPlugin\Provider\Data; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException; diff --git a/src/Provider/AkeneoAttributePropertiesProvider.php b/src/Provider/Data/AkeneoAttributePropertiesProvider.php similarity index 97% rename from src/Provider/AkeneoAttributePropertiesProvider.php rename to src/Provider/Data/AkeneoAttributePropertiesProvider.php index f2dfcc41..9f9d2056 100644 --- a/src/Provider/AkeneoAttributePropertiesProvider.php +++ b/src/Provider/Data/AkeneoAttributePropertiesProvider.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Synolia\SyliusAkeneoPlugin\Provider; +namespace Synolia\SyliusAkeneoPlugin\Provider\Data; use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; diff --git a/src/Provider/AkeneoReferenceEntityAttributeDataProvider.php b/src/Provider/Data/AkeneoReferenceEntityAttributeDataProvider.php similarity index 97% rename from src/Provider/AkeneoReferenceEntityAttributeDataProvider.php rename to src/Provider/Data/AkeneoReferenceEntityAttributeDataProvider.php index 280721e8..713f2ca1 100644 --- a/src/Provider/AkeneoReferenceEntityAttributeDataProvider.php +++ b/src/Provider/Data/AkeneoReferenceEntityAttributeDataProvider.php @@ -2,12 +2,13 @@ declare(strict_types=1); -namespace Synolia\SyliusAkeneoPlugin\Provider; +namespace Synolia\SyliusAkeneoPlugin\Provider\Data; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingLocaleTranslationOrScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\MissingScopeException; use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\TranslationNotFoundException; +use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider; final class AkeneoReferenceEntityAttributeDataProvider implements AkeneoReferenceEntityAttributeDataProviderInterface { diff --git a/src/Provider/AkeneoReferenceEntityAttributeDataProviderInterface.php b/src/Provider/Data/AkeneoReferenceEntityAttributeDataProviderInterface.php similarity index 88% rename from src/Provider/AkeneoReferenceEntityAttributeDataProviderInterface.php rename to src/Provider/Data/AkeneoReferenceEntityAttributeDataProviderInterface.php index 2e81a93a..9d8802d2 100644 --- a/src/Provider/AkeneoReferenceEntityAttributeDataProviderInterface.php +++ b/src/Provider/Data/AkeneoReferenceEntityAttributeDataProviderInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Synolia\SyliusAkeneoPlugin\Provider; +namespace Synolia\SyliusAkeneoPlugin\Provider\Data; interface AkeneoReferenceEntityAttributeDataProviderInterface { diff --git a/src/Provider/AkeneoReferenceEntityAttributePropertiesProvider.php b/src/Provider/Data/AkeneoReferenceEntityAttributePropertiesProvider.php similarity index 96% rename from src/Provider/AkeneoReferenceEntityAttributePropertiesProvider.php rename to src/Provider/Data/AkeneoReferenceEntityAttributePropertiesProvider.php index bc5d6f11..a9205e7e 100644 --- a/src/Provider/AkeneoReferenceEntityAttributePropertiesProvider.php +++ b/src/Provider/Data/AkeneoReferenceEntityAttributePropertiesProvider.php @@ -2,9 +2,10 @@ declare(strict_types=1); -namespace Synolia\SyliusAkeneoPlugin\Provider; +namespace Synolia\SyliusAkeneoPlugin\Provider\Data; use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; +use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider; final class AkeneoReferenceEntityAttributePropertiesProvider { diff --git a/src/Provider/Handler/Task/TaskHandlerProvider.php b/src/Provider/Handler/Task/TaskHandlerProvider.php new file mode 100644 index 00000000..3f324b81 --- /dev/null +++ b/src/Provider/Handler/Task/TaskHandlerProvider.php @@ -0,0 +1,36 @@ +taskHandlers as $taskHandler) { + if ($taskHandler->support($pipelinePayload)) { + $this->akeneoLogger->notice($taskHandler::class); + + return $taskHandler; + } + } + + throw new \LogicException('No supported TaskHandler found'); + } +} diff --git a/src/Provider/Handler/Task/TaskHandlerProviderInterface.php b/src/Provider/Handler/Task/TaskHandlerProviderInterface.php new file mode 100644 index 00000000..97399c6e --- /dev/null +++ b/src/Provider/Handler/Task/TaskHandlerProviderInterface.php @@ -0,0 +1,13 @@ +task; + } +} diff --git a/src/Provider/Task/Batch/AssociationTypeBatchTaskProvider.php b/src/Provider/Task/Batch/AssociationTypeBatchTaskProvider.php new file mode 100644 index 00000000..982e289a --- /dev/null +++ b/src/Provider/Task/Batch/AssociationTypeBatchTaskProvider.php @@ -0,0 +1,27 @@ +task; + } +} diff --git a/src/Provider/Task/Batch/AttributeBatchTaskProvider.php b/src/Provider/Task/Batch/AttributeBatchTaskProvider.php new file mode 100644 index 00000000..0335a385 --- /dev/null +++ b/src/Provider/Task/Batch/AttributeBatchTaskProvider.php @@ -0,0 +1,27 @@ +task; + } +} diff --git a/src/Provider/Task/Batch/BatchTaskProvider.php b/src/Provider/Task/Batch/BatchTaskProvider.php new file mode 100644 index 00000000..4fc46650 --- /dev/null +++ b/src/Provider/Task/Batch/BatchTaskProvider.php @@ -0,0 +1,32 @@ +providers as $provider) { + if ($provider->support($payload)) { + return $provider->getTask(); + } + } + + throw new \LogicException('No batch task found'); + } +} diff --git a/src/Provider/Task/Batch/BatchTaskProviderInterface.php b/src/Provider/Task/Batch/BatchTaskProviderInterface.php new file mode 100644 index 00000000..a6d6cf2d --- /dev/null +++ b/src/Provider/Task/Batch/BatchTaskProviderInterface.php @@ -0,0 +1,17 @@ +providers as $provider) { + if ($provider->support($payload)) { + return $provider->createBatchPayload($payload); + } + } + + throw new \InvalidArgumentException(''); + } +} diff --git a/src/Provider/Task/Batch/Payload/PayloadBatchTaskProviderInterface.php b/src/Provider/Task/Batch/Payload/PayloadBatchTaskProviderInterface.php new file mode 100644 index 00000000..45725461 --- /dev/null +++ b/src/Provider/Task/Batch/Payload/PayloadBatchTaskProviderInterface.php @@ -0,0 +1,16 @@ +hasCommandContext()) ? $payload->getCommandContext() : null; + + return new ProductModelPayload($payload->getAkeneoPimClient(), $commandContext); + } + + public function support(PipelinePayloadInterface $pipelinePayload): bool + { + return $pipelinePayload instanceof ProductModelPayload; + } +} diff --git a/src/Provider/Task/Batch/ProductModelBatchTaskProvider.php b/src/Provider/Task/Batch/ProductModelBatchTaskProvider.php new file mode 100644 index 00000000..5b8b5803 --- /dev/null +++ b/src/Provider/Task/Batch/ProductModelBatchTaskProvider.php @@ -0,0 +1,27 @@ +task; + } +} diff --git a/src/Provider/Task/Batch/ProductVariantBatchTaskProvider.php b/src/Provider/Task/Batch/ProductVariantBatchTaskProvider.php new file mode 100644 index 00000000..24847d33 --- /dev/null +++ b/src/Provider/Task/Batch/ProductVariantBatchTaskProvider.php @@ -0,0 +1,27 @@ +task; + } +} diff --git a/src/Resources/config/processors.yaml b/src/Resources/config/processors.yaml index 4a9708f0..7cf602b8 100644 --- a/src/Resources/config/processors.yaml +++ b/src/Resources/config/processors.yaml @@ -29,7 +29,7 @@ services: # ProductOptionValue Processors Synolia\SyliusAkeneoPlugin\Provider\OptionValuesProcessorProviderInterface: - class: Synolia\SyliusAkeneoPlugin\Provider\ChainOptionValuesDataProvider + class: Synolia\SyliusAkeneoPlugin\Provider\ChainOptionValuesProcessorProvider arguments: - !tagged_iterator { tag: !php/const Synolia\SyliusAkeneoPlugin\Processor\ProductOptionValue\OptionValuesProcessorInterface::TAG_ID, default_priority_method: getDefaultPriority } diff --git a/src/Task/Asset/ProcessAssetTask.php b/src/Task/Asset/ProcessAssetTask.php index 26bf8d10..95dd007c 100644 --- a/src/Task/Asset/ProcessAssetTask.php +++ b/src/Task/Asset/ProcessAssetTask.php @@ -4,32 +4,27 @@ namespace Synolia\SyliusAkeneoPlugin\Task\Asset; -use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Synolia\SyliusAkeneoPlugin\Checker\EditionCheckerInterface; use Synolia\SyliusAkeneoPlugin\Logger\Messages; -use Synolia\SyliusAkeneoPlugin\Manager\ProcessManagerInterface; use Synolia\SyliusAkeneoPlugin\Payload\Asset\AssetPayload; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; -use Synolia\SyliusAkeneoPlugin\Task\AbstractProcessTask; +use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; +use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; +use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; -final class ProcessAssetTask extends AbstractProcessTask +final class ProcessAssetTask implements AkeneoTaskInterface { + use TaskHandlerTrait{ + TaskHandlerTrait::__construct as private __taskHandlerConstruct; + } + public function __construct( - EntityManagerInterface $entityManager, - LoggerInterface $akeneoLogger, - ProcessManagerInterface $processManager, - BatchAssetTask $task, private EditionCheckerInterface $editionChecker, - string $projectDir, + private LoggerInterface $akeneoLogger, + private TaskHandlerProviderInterface $taskHandlerProvider, ) { - parent::__construct( - $entityManager, - $processManager, - $task, - $akeneoLogger, - $projectDir, - ); + $this->__taskHandlerConstruct($taskHandlerProvider); } /** @@ -40,32 +35,24 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $isEnterprise = $this->editionChecker->isEnterprise() || $this->editionChecker->isSerenityEdition(); if (!$isEnterprise) { - $this->logger->warning('Skipped akeneo:import:assets command because the configured Akeneo edition is not compatible.'); + $this->akeneoLogger->warning('Skipped akeneo:import:assets command because the configured Akeneo edition is not compatible.'); return $payload; } - $this->logger->debug(self::class); + $this->akeneoLogger->debug(self::class); if ($payload->isContinue()) { - $this->process($payload); + $this->continue($payload); return $payload; } - $this->logger->notice(Messages::retrieveFromAPI($payload->getType())); + $this->akeneoLogger->notice(Messages::retrieveFromAPI($payload->getType())); $resources = $payload->getAkeneoPimClient()->getAssetFamilyApi()->all(); $this->handle($payload, $resources); - $this->processManager->waitForAllProcesses(); return $payload; } - - protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface - { - $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null; - - return new AssetPayload($payload->getAkeneoPimClient(), $commandContext); - } } diff --git a/src/Task/AssociationType/BatchAssociationTypesTask.php b/src/Task/AssociationType/BatchAssociationTypesTask.php index 47752d8c..3e769bbf 100644 --- a/src/Task/AssociationType/BatchAssociationTypesTask.php +++ b/src/Task/AssociationType/BatchAssociationTypesTask.php @@ -4,34 +4,23 @@ namespace Synolia\SyliusAkeneoPlugin\Task\AssociationType; -use Akeneo\Pim\ApiClient\Exception\NotFoundHttpException; use Doctrine\DBAL\Result; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; -use Sylius\Component\Product\Model\ProductAssociationTypeInterface; -use Sylius\Component\Product\Repository\ProductAssociationTypeRepositoryInterface; -use Sylius\Component\Resource\Factory\FactoryInterface; -use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\ExcludedAttributeException; -use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\InvalidAttributeException; -use Synolia\SyliusAkeneoPlugin\Exceptions\NoAttributeResourcesException; -use Synolia\SyliusAkeneoPlugin\Exceptions\UnsupportedAttributeTypeException; use Synolia\SyliusAkeneoPlugin\Logger\Messages; use Synolia\SyliusAkeneoPlugin\Payload\Association\AssociationTypePayload; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; -use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider; +use Synolia\SyliusAkeneoPlugin\Processor\Resource\Attribute\AttributeResourceProcessor; +use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException; use Synolia\SyliusAkeneoPlugin\Task\AbstractBatchTask; use Throwable; final class BatchAssociationTypesTask extends AbstractBatchTask { - private string $type; - public function __construct( EntityManagerInterface $entityManager, - private LoggerInterface $logger, - private FactoryInterface $productAssociationTypeFactory, - private ProductAssociationTypeRepositoryInterface $productAssociationTypeRepository, - private SyliusAkeneoLocaleCodeProvider $syliusAkeneoLocaleCodeProvider, + private LoggerInterface $akeneoLogger, + private AttributeResourceProcessor $resourceProcessor, ) { parent::__construct($entityManager); } @@ -39,94 +28,33 @@ public function __construct( /** * @param AssociationTypePayload $payload * - * @throws NoAttributeResourcesException * @throws Throwable */ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface { - $this->logger->debug(self::class); - $this->type = $payload->getType(); - $this->logger->notice(Messages::createOrUpdate($this->type)); - - try { - $this->entityManager->beginTransaction(); - - $query = $this->getSelectStatement($payload); - /** @var Result $queryResult */ - $queryResult = $query->executeQuery(); - - while ($results = $queryResult->fetchAllAssociative()) { - foreach ($results as $result) { - /** @var array{code: string, labels: array} $resource */ - $resource = json_decode($result['values'], true, 512, \JSON_THROW_ON_ERROR); - - try { - if (!$this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->beginTransaction(); - } - - $productAssociationType = $this->productAssociationTypeRepository->findOneBy(['code' => $resource['code']]); - if (!$productAssociationType instanceof ProductAssociationTypeInterface) { - /** @var ProductAssociationTypeInterface $productAssociationType */ - $productAssociationType = $this->productAssociationTypeFactory->createNew(); - $this->entityManager->persist($productAssociationType); - - $productAssociationType->setCode($resource['code']); - } - - $this->setTranslations($resource['labels'], $productAssociationType); - - $this->entityManager->flush(); - - if ($this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->commit(); - } - - $this->entityManager->clear(); - unset($resource, $productAssociationType); - - $this->removeEntry($payload, (int) $result['id']); - } catch (UnsupportedAttributeTypeException | InvalidAttributeException | ExcludedAttributeException | NotFoundHttpException $throwable) { - $this->removeEntry($payload, (int) $result['id']); - } catch (Throwable $throwable) { - if ($this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->rollback(); - } - $this->logger->warning($throwable->getMessage()); - } + $this->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()) { + 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; } } - - if ($this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->commit(); - } - } catch (Throwable $throwable) { - if ($this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->rollback(); - } - $this->logger->warning($throwable->getMessage()); - - throw $throwable; } return $payload; } - - private function setTranslations(array $labels, ProductAssociationTypeInterface $productAssociationType): void - { - foreach ($this->syliusAkeneoLocaleCodeProvider->getUsedLocalesOnBothPlatforms() as $usedLocalesOnBothPlatform) { - $akeneoLocale = $this->syliusAkeneoLocaleCodeProvider->getAkeneoLocale($usedLocalesOnBothPlatform); - - $productAssociationType->setCurrentLocale($usedLocalesOnBothPlatform); - $productAssociationType->setFallbackLocale($usedLocalesOnBothPlatform); - - if (!isset($labels[$akeneoLocale])) { - $productAssociationType->setName(sprintf('[%s]', $productAssociationType->getCode())); - - continue; - } - - $productAssociationType->setName($labels[$akeneoLocale]); - } - } } diff --git a/src/Task/AssociationType/ProcessAssociationTypeTask.php b/src/Task/AssociationType/ProcessAssociationTypeTask.php index 59490c86..6e581d4f 100644 --- a/src/Task/AssociationType/ProcessAssociationTypeTask.php +++ b/src/Task/AssociationType/ProcessAssociationTypeTask.php @@ -4,29 +4,30 @@ namespace Synolia\SyliusAkeneoPlugin\Task\AssociationType; -use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Synolia\SyliusAkeneoPlugin\Event\FilterEvent; use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException; -use Synolia\SyliusAkeneoPlugin\Manager\ProcessManagerInterface; use Synolia\SyliusAkeneoPlugin\Payload\Association\AssociationTypePayload; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface; -use Synolia\SyliusAkeneoPlugin\Task\AbstractProcessTask; +use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; +use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; +use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; -final class ProcessAssociationTypeTask extends AbstractProcessTask +final class ProcessAssociationTypeTask implements AkeneoTaskInterface { + use TaskHandlerTrait{ + TaskHandlerTrait::__construct as private __taskHandlerConstruct; + } + public function __construct( - EntityManagerInterface $entityManager, - LoggerInterface $akeneoLogger, - ProcessManagerInterface $processManager, - BatchAssociationTypesTask $task, private ApiConnectionProviderInterface $apiConnectionProvider, private EventDispatcherInterface $eventDispatcher, - string $projectDir, + private LoggerInterface $logger, + private TaskHandlerProviderInterface $taskHandlerProvider, ) { - parent::__construct($entityManager, $processManager, $task, $akeneoLogger, $projectDir); + $this->__taskHandlerConstruct($taskHandlerProvider); } /** @@ -38,7 +39,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $this->logger->debug(self::class); if ($payload->isContinue()) { - $this->process($payload); + $this->continue($payload); return $payload; } @@ -60,15 +61,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte ); $this->handle($payload, $page); - $this->processManager->waitForAllProcesses(); return $payload; } - - protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface - { - $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null; - - return new AssociationTypePayload($payload->getAkeneoPimClient(), $commandContext); - } } diff --git a/src/Task/Attribute/BatchAttributesTask.php b/src/Task/Attribute/BatchAttributesTask.php index fa2eca29..4d396eb9 100644 --- a/src/Task/Attribute/BatchAttributesTask.php +++ b/src/Task/Attribute/BatchAttributesTask.php @@ -4,42 +4,22 @@ namespace Synolia\SyliusAkeneoPlugin\Task\Attribute; -use Akeneo\Pim\ApiClient\Exception\NotFoundHttpException; use Doctrine\DBAL\Result; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Synolia\SyliusAkeneoPlugin\Creator\AttributeCreatorInterface; -use Synolia\SyliusAkeneoPlugin\Event\Attribute\AfterProcessingAttributeEvent; -use Synolia\SyliusAkeneoPlugin\Event\Attribute\BeforeProcessingAttributeEvent; -use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\ExcludedAttributeException; -use Synolia\SyliusAkeneoPlugin\Exceptions\Attribute\InvalidAttributeException; -use Synolia\SyliusAkeneoPlugin\Exceptions\NoAttributeResourcesException; -use Synolia\SyliusAkeneoPlugin\Exceptions\UnsupportedAttributeTypeException; use Synolia\SyliusAkeneoPlugin\Logger\Messages; -use Synolia\SyliusAkeneoPlugin\Payload\AbstractPayload; use Synolia\SyliusAkeneoPlugin\Payload\Attribute\AttributePayload; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; -use Synolia\SyliusAkeneoPlugin\Processor\ProductAttribute\ProductAttributeChoiceProcessorInterface; -use Synolia\SyliusAkeneoPlugin\Processor\ProductAttribute\ProductAttributeTableProcessorInterface; -use Synolia\SyliusAkeneoPlugin\Processor\ProductOption\ProductOptionProcessorInterface; -use Synolia\SyliusAkeneoPlugin\Retriever\FamilyRetrieverInterface; -use Synolia\SyliusAkeneoPlugin\Retriever\FamilyVariantRetrieverInterface; +use Synolia\SyliusAkeneoPlugin\Processor\Resource\Attribute\AttributeResourceProcessor; +use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException; use Synolia\SyliusAkeneoPlugin\Task\AbstractBatchTask; -use Webmozart\Assert\Assert; final class BatchAttributesTask extends AbstractBatchTask { public function __construct( EntityManagerInterface $entityManager, - private LoggerInterface $logger, - private ProductAttributeChoiceProcessorInterface $attributeChoiceProcessor, - private ProductOptionProcessorInterface $productOptionProcessor, - private ProductAttributeTableProcessorInterface $productAttributeTableProcessor, - private EventDispatcherInterface $dispatcher, - private AttributeCreatorInterface $attributeCreator, - private FamilyRetrieverInterface $familyRetriever, - private FamilyVariantRetrieverInterface $familyVariantRetriever, + private LoggerInterface $akeneoLogger, + private AttributeResourceProcessor $resourceProcessor, ) { parent::__construct($entityManager); } @@ -47,116 +27,33 @@ public function __construct( /** * @param AttributePayload $payload * - * @throws NoAttributeResourcesException * @throws \Throwable */ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface { - $this->logger->debug(self::class); + $this->akeneoLogger->debug(self::class); $type = $payload->getType(); - $this->logger->notice(Messages::createOrUpdate($type)); - - try { - $this->entityManager->beginTransaction(); - - $query = $this->getSelectStatement($payload); - /** @var Result $queryResult */ - $queryResult = $query->executeQuery(); - - $variationAxes = array_unique($this->getVariationAxes($payload)); - while ($results = $queryResult->fetchAllAssociative()) { - foreach ($results as $result) { - $resource = json_decode($result['values'], true, 512, \JSON_THROW_ON_ERROR); - - if (!is_array($resource)) { - throw new InvalidAttributeException(); - } - - try { - $this->dispatcher->dispatch(new BeforeProcessingAttributeEvent($resource)); - - if (!$this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->beginTransaction(); - } - - $attribute = $this->attributeCreator->create($resource); - $this->entityManager->flush(); - - //Handle attribute options - $this->attributeChoiceProcessor->process($attribute, $resource); - - //Handle attribute table configuration - $this->productAttributeTableProcessor->process($attribute, $resource); - - //Handler options - $this->productOptionProcessor->process($attribute, $variationAxes); - - $this->dispatcher->dispatch(new AfterProcessingAttributeEvent($resource, $attribute)); - - $this->entityManager->flush(); - if ($this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->commit(); - } - - $this->entityManager->clear(); - unset($resource, $attribute); - - $this->removeEntry($payload, (int) $result['id']); - } catch (UnsupportedAttributeTypeException | InvalidAttributeException | ExcludedAttributeException | NotFoundHttpException $throwable) { - $this->removeEntry($payload, (int) $result['id']); - } catch (\Throwable $throwable) { - if ($this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->rollback(); - } - $this->logger->warning($throwable->getMessage()); - } + $this->akeneoLogger->notice(Messages::createOrUpdate($type)); + + $query = $this->getSelectStatement($payload); + /** @var Result $queryResult */ + $queryResult = $query->executeQuery(); + + while ($results = $queryResult->fetchAllAssociative()) { + 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; } } - - if ($this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->commit(); - } - } catch (\Throwable $throwable) { - if ($this->entityManager->getConnection()->isTransactionActive()) { - $this->entityManager->rollback(); - } - $this->logger->warning($throwable->getMessage()); - - throw $throwable; } return $payload; } - - private function getVariationAxes(PipelinePayloadInterface $payload): array - { - Assert::isInstanceOf($payload, AbstractPayload::class); - $variationAxes = []; - $families = $this->familyRetriever->getFamilies(); - - foreach ($families as $family) { - $familyVariants = $this->familyVariantRetriever->getVariants($family['code']); - - $variationAxes = array_merge($variationAxes, $this->getVariationAxesForFamilies($familyVariants)); - } - - return $variationAxes; - } - - private function getVariationAxesForFamilies(array $familyVariants): array - { - $variationAxes = []; - - /** @var array{variant_attribute_sets: array} $familyVariant */ - foreach ($familyVariants as $familyVariant) { - /** @var array{axes: array} $variantAttributeSet */ - foreach ($familyVariant['variant_attribute_sets'] as $variantAttributeSet) { - foreach ($variantAttributeSet['axes'] as $axe) { - $variationAxes[] = $axe; - } - } - } - - return $variationAxes; - } } diff --git a/src/Task/Attribute/ProcessAttributeTask.php b/src/Task/Attribute/ProcessAttributeTask.php index 127d6d26..48a77afa 100644 --- a/src/Task/Attribute/ProcessAttributeTask.php +++ b/src/Task/Attribute/ProcessAttributeTask.php @@ -4,29 +4,29 @@ namespace Synolia\SyliusAkeneoPlugin\Task\Attribute; -use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Synolia\SyliusAkeneoPlugin\Event\FilterEvent; use Synolia\SyliusAkeneoPlugin\Exceptions\Payload\CommandContextIsNullException; -use Synolia\SyliusAkeneoPlugin\Manager\ProcessManagerInterface; -use Synolia\SyliusAkeneoPlugin\Payload\Attribute\AttributePayload; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface; -use Synolia\SyliusAkeneoPlugin\Task\AbstractProcessTask; +use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; +use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; +use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; -final class ProcessAttributeTask extends AbstractProcessTask +final class ProcessAttributeTask implements AkeneoTaskInterface { + use TaskHandlerTrait{ + TaskHandlerTrait::__construct as private __taskHandlerConstruct; + } + public function __construct( - EntityManagerInterface $entityManager, - LoggerInterface $akeneoLogger, - ProcessManagerInterface $processManager, - BatchAttributesTask $task, private ApiConnectionProviderInterface $apiConnectionProvider, private EventDispatcherInterface $eventDispatcher, - string $projectDir, + private LoggerInterface $logger, + private TaskHandlerProviderInterface $taskHandlerProvider, ) { - parent::__construct($entityManager, $processManager, $task, $akeneoLogger, $projectDir); + $this->__taskHandlerConstruct($taskHandlerProvider); } /** @@ -38,7 +38,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $this->logger->debug(self::class); if ($payload->isContinue()) { - $this->process($payload); + $this->continue($payload); return $payload; } @@ -63,15 +63,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte ); $this->handle($payload, $page); - $this->processManager->waitForAllProcesses(); return $payload; } - - protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface - { - $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null; - - return new AttributePayload($payload->getAkeneoPimClient(), $commandContext); - } } diff --git a/src/Task/Option/DeleteTask.php b/src/Task/Option/DeleteTask.php deleted file mode 100644 index 5e7065e3..00000000 --- a/src/Task/Option/DeleteTask.php +++ /dev/null @@ -1,96 +0,0 @@ -logger->debug(self::class); - $this->logger->notice(Messages::removalNoLongerExist($payload->getType())); - $this->type = $payload->getType(); - - if (!$this->productAttributeRepository instanceof ProductAttributeRepository) { - throw new LogicException('Wrong repository instance provided.'); - } - if (!$this->productOptionRepository instanceof ProductOptionRepository) { - throw new LogicException('Wrong repository instance provided.'); - } - - try { - $this->entityManager->beginTransaction(); - - $this->process(); - - $this->entityManager->flush(); - $this->entityManager->commit(); - } catch (Throwable $throwable) { - $this->entityManager->rollback(); - $this->logger->warning($throwable->getMessage()); - - throw $throwable; - } - - $this->logger->notice(Messages::countOfDeleted($payload->getType(), $this->deleteCount)); - - return $payload; - } - - private function process(): void - { - $attributeCodes = $this->productAttributeRepository->getAllAttributeCodes(); - $removedOptionIds = $this->productOptionRepository->getRemovedOptionIds($attributeCodes); - - /** @var class-string $productOptionClass */ - $productOptionClass = $this->parameterBag->get('sylius.model.product_option.class'); - if (!class_exists($productOptionClass)) { - throw new LogicException('ProductOption class does not exist.'); - } - - foreach ($removedOptionIds as $removedOptionId) { - /** @var ProductOption $referenceEntity */ - $referenceEntity = $this->entityManager->getReference($productOptionClass, $removedOptionId); - if (null !== $referenceEntity) { - /** @var ProductOptionValueInterface $optionValue */ - foreach ($referenceEntity->getValues() as $optionValue) { - $this->entityManager->remove($optionValue); - } - $this->entityManager->remove($referenceEntity); - $this->logger->info(Messages::hasBeenDeleted($this->type, (string) $referenceEntity->getCode())); - ++$this->deleteCount; - } - } - } -} diff --git a/src/Task/Product/AbstractCreateProductEntities.php b/src/Task/Product/AbstractCreateProductEntities.php deleted file mode 100644 index d1fb5b41..00000000 --- a/src/Task/Product/AbstractCreateProductEntities.php +++ /dev/null @@ -1,51 +0,0 @@ -productVariantRepository->findOneBy(['code' => $product->getCode()]); - - if (!$productVariant instanceof ProductVariantInterface) { - $productVariant = $this->productVariantFactory->createForProduct($product); - $productVariant->setCode($product->getCode()); - - $this->entityManager->persist($productVariant); - } - - return $productVariant; - } -} diff --git a/src/Task/Product/BatchProductsTask.php b/src/Task/Product/BatchProductsTask.php index 7a84f03a..79d1e3fc 100644 --- a/src/Task/Product/BatchProductsTask.php +++ b/src/Task/Product/BatchProductsTask.php @@ -6,57 +6,38 @@ use Doctrine\DBAL\Result; use Doctrine\ORM\EntityManagerInterface; -use Psr\Log\LoggerInterface; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; -use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload; +use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException; +use Synolia\SyliusAkeneoPlugin\Processor\Resource\ProductVariant\ProductVariantResourceProcessor; use Synolia\SyliusAkeneoPlugin\Task\AbstractBatchTask; -use Throwable; final class BatchProductsTask extends AbstractBatchTask { public function __construct( EntityManagerInterface $entityManager, - private SimpleProductTask $batchSimpleProductTask, - private ConfigurableProductsTask $batchConfigurableProductsTask, - private LoggerInterface $logger, + private ProductVariantResourceProcessor $resourceProcessor, ) { parent::__construct($entityManager); } public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface { - if (!$payload instanceof ProductPayload) { - return $payload; - } - $query = $this->getSelectStatement($payload); /** @var Result $queryResult */ $queryResult = $query->executeQuery(); while ($results = $queryResult->fetchAllAssociative()) { foreach ($results as $result) { - try { - /** @var array{identifier: string,parent: string|null} $resource */ - $resource = json_decode($result['values'], true, 512, \JSON_THROW_ON_ERROR); + /** @var array $resource */ + $resource = json_decode($result['values'], true); - $isSimple = null === $resource['parent']; - if ($isSimple) { - $this->logger->debug('Processing Simple Product', [ - 'parent' => $resource['parent'], - 'code' => $resource['identifier'], - ]); - $this->batchSimpleProductTask->__invoke($payload, $resource); - } else { - $this->logger->debug('Processing Configurable Product', [ - 'parent' => $resource['parent'], - 'code' => $resource['identifier'], - ]); - $this->batchConfigurableProductsTask->__invoke($payload, $resource); - } - - $this->removeEntry($payload, (int) $result['id']); - } catch (Throwable) { + try { + $this->resourceProcessor->process($resource); + unset($resource); $this->removeEntry($payload, (int) $result['id']); + } catch (MaxResourceProcessorRetryException) { + // Skip the failing line + continue; } } } diff --git a/src/Task/Product/ProcessProductsTask.php b/src/Task/Product/ProcessProductsTask.php index 0c11a578..280f536f 100644 --- a/src/Task/Product/ProcessProductsTask.php +++ b/src/Task/Product/ProcessProductsTask.php @@ -4,43 +4,36 @@ namespace Synolia\SyliusAkeneoPlugin\Task\Product; -use Akeneo\Pim\ApiClient\Pagination\Page; -use Akeneo\Pim\ApiClient\Pagination\PageInterface; -use Doctrine\DBAL\ParameterType; -use Doctrine\ORM\EntityManagerInterface; 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\Manager\ProcessManagerInterface; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface; -use Synolia\SyliusAkeneoPlugin\Task\AbstractProcessTask; +use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; +use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; +use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; -final class ProcessProductsTask extends AbstractProcessTask +final class ProcessProductsTask implements AkeneoTaskInterface { + use TaskHandlerTrait{ + TaskHandlerTrait::__construct as private __taskHandlerConstruct; + } + public function __construct( - EntityManagerInterface $entityManager, - LoggerInterface $akeneoLogger, - ProcessManagerInterface $processManager, - BatchProductsTask $task, private ProductFilterInterface $productFilter, private ApiConnectionProviderInterface $apiConnectionProvider, private EventDispatcherInterface $eventDispatcher, - string $projectDir, + private LoggerInterface $logger, + private TaskHandlerProviderInterface $taskHandlerProvider, ) { - parent::__construct($entityManager, $processManager, $task, $akeneoLogger, $projectDir); + $this->__taskHandlerConstruct($taskHandlerProvider); } /** - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * - * @TODO Probably need to be refactored - * * @param ProductPayload $payload */ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface @@ -48,7 +41,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $this->logger->debug(self::class); if ($payload->isContinue()) { - $this->process($payload); + $this->continue($payload); return $payload; } @@ -69,89 +62,14 @@ 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( $this->apiConnectionProvider->get()->getPaginationSize(), true, $queryParameters, ); - if (!$resources instanceof Page) { - return $payload; - } - - $count = 0; - $ids = []; - - $this->handleProducts($payload, $resources, $count, $ids); - - if ($count > 0 && count($ids) > 0 && $payload->isBatchingAllowed() && $payload->getProcessAsSoonAsPossible() && $payload->allowParallel()) { - $this->logger->notice('Batching', ['from_id' => $ids[0], 'to_id' => $ids[(is_countable($ids) ? \count($ids) : 0) - 1]]); - $this->batch($payload, $ids); - } - - if ($count > 0 && count($ids) > 0 && $payload->isBatchingAllowed() && $payload->getProcessAsSoonAsPossible() && !$payload->allowParallel()) { - $payload->setIds($ids); - $this->task->__invoke($payload); - } - - if ($count > 0 && count($ids) > 0 && !$payload->isBatchingAllowed()) { - $payload->setIds($ids); - $this->task->__invoke($payload); - } - - if ($count > 0 && !$payload->getProcessAsSoonAsPossible()) { - $this->process($payload); - } - - $this->processManager->startAll(); + $this->handle($payload, $resources); return $payload; } - - private function handleProducts( - PipelinePayloadInterface $payload, - PageInterface $page, - int &$count = 0, - array &$ids = [], - ): void { - $this->processManager->setInstantProcessing($payload->getProcessAsSoonAsPossible()); - $this->processManager->setNumberOfParallelProcesses($payload->getMaxRunningProcessQueueSize()); - - while ( - ($page instanceof Page && $page->hasNextPage()) || - ($page instanceof Page && !$page->hasPreviousPage()) || - $page instanceof Page - ) { - foreach ($page->getItems() as $item) { - $sql = sprintf( - 'INSERT INTO `%s` (`values`, `is_simple`) VALUES (:values, :is_simple);', - ProductPayload::TEMP_AKENEO_TABLE_NAME, - ); - - $stmt = $this->entityManager->getConnection()->prepare($sql); - $stmt->bindValue('values', json_encode($item, \JSON_THROW_ON_ERROR)); - $stmt->bindValue('is_simple', null === $item['parent'], ParameterType::BOOLEAN); - $stmt->execute(); - ++$count; - - $ids[] = $this->entityManager->getConnection()->lastInsertId(); - - if ($payload->getProcessAsSoonAsPossible() && $payload->allowParallel() && 0 === $count % $payload->getBatchSize()) { - $this->logger->notice('Batching', ['from_id' => $ids[0], 'to_id' => $ids[\count($ids) - 1]]); - $this->batch($payload, $ids); - $ids = []; - } - } - - $page = $page->getNextPage(); - } - } - - protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface - { - $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null; - - return new ProductPayload($payload->getAkeneoPimClient(), $commandContext); - } } diff --git a/src/Task/Product/SetupProductTask.php b/src/Task/Product/SetupProductTask.php deleted file mode 100644 index bc7ca63d..00000000 --- a/src/Task/Product/SetupProductTask.php +++ /dev/null @@ -1,44 +0,0 @@ -isContinue()) { - $schemaManager = $this->entityManager->getConnection()->getSchemaManager(); - $tableExist = $schemaManager->tablesExist([ProductPayload::TEMP_AKENEO_TABLE_NAME]); - - if (true === $tableExist) { - return $payload; - } - } - - $this->taskProvider->get(TearDownProductTask::class)->__invoke($payload); - - $query = sprintf( - 'CREATE TABLE `%s` ( - `id` INT NOT NULL AUTO_INCREMENT, - `values` JSON NULL, - `is_simple` TINYINT NOT NULL, - PRIMARY KEY (`id`));', - ProductPayload::TEMP_AKENEO_TABLE_NAME, - ); - $this->entityManager->getConnection()->executeStatement($query); - - return $payload; - } -} diff --git a/src/Task/Product/TearDownProductTask.php b/src/Task/Product/TearDownProductTask.php deleted file mode 100644 index d3f3cea4..00000000 --- a/src/Task/Product/TearDownProductTask.php +++ /dev/null @@ -1,38 +0,0 @@ -delete(); - } catch (ConnectionLost) { - $this->delete(); - } - - return $payload; - } - - private function delete(): void - { - $exists = $this->entityManager->getConnection()->getSchemaManager()->tablesExist([ProductPayload::TEMP_AKENEO_TABLE_NAME]); - - if ($exists) { - $this->entityManager->getConnection()->getSchemaManager()->dropTable(ProductPayload::TEMP_AKENEO_TABLE_NAME); - } - } -} diff --git a/src/Task/ProductModel/BatchProductModelTask.php b/src/Task/ProductModel/BatchProductModelTask.php index a5a1246d..eaae613a 100644 --- a/src/Task/ProductModel/BatchProductModelTask.php +++ b/src/Task/ProductModel/BatchProductModelTask.php @@ -6,43 +6,22 @@ use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\ORMInvalidArgumentException; -use Doctrine\Persistence\ManagerRegistry; use Psr\Log\LoggerInterface; -use Sylius\Component\Core\Model\ProductInterface; -use Sylius\Component\Core\Repository\ProductRepositoryInterface; -use Sylius\Component\Product\Factory\ProductFactoryInterface; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Synolia\SyliusAkeneoPlugin\Checker\Product\IsProductProcessableCheckerInterface; -use Synolia\SyliusAkeneoPlugin\Event\Product\AfterProcessingProductEvent; -use Synolia\SyliusAkeneoPlugin\Event\Product\BeforeProcessingProductEvent; -use Synolia\SyliusAkeneoPlugin\Logger\Messages; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload; -use Synolia\SyliusAkeneoPlugin\Processor\Product\ProductProcessorChainInterface; -use Synolia\SyliusAkeneoPlugin\Processor\ProductGroup\ProductGroupProcessor; +use Synolia\SyliusAkeneoPlugin\Processor\Resource\Exception\MaxResourceProcessorRetryException; +use Synolia\SyliusAkeneoPlugin\Processor\Resource\Product\ProductModelResourceProcessor; use Synolia\SyliusAkeneoPlugin\Task\AbstractBatchTask; final class BatchProductModelTask extends AbstractBatchTask { - private string $type; - /** * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( EntityManagerInterface $entityManager, - private ProductFactoryInterface $productFactory, - private ProductRepositoryInterface $productRepository, private LoggerInterface $logger, - private EventDispatcherInterface $dispatcher, - private ProductProcessorChainInterface $productProcessorChain, - private IsProductProcessableCheckerInterface $isProductProcessableChecker, - private ProductGroupProcessor $productGroupProcessor, - private ManagerRegistry $managerRegistry, - private int $maxRetryCount, - private int $retryWaitTime, - private int $retryCount = 0, + private ProductModelResourceProcessor $resourceProcessor, ) { parent::__construct($entityManager); } @@ -54,126 +33,27 @@ public function __construct( */ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface { - if ($this->retryCount === $this->maxRetryCount) { - return $payload; - } - $this->logger->debug(self::class); - $this->type = $payload->getType(); $query = $this->getSelectStatement($payload); $queryResult = $query->executeQuery(); while ($results = $queryResult->fetchAllAssociative()) { foreach ($results as $result) { - $isSuccess = false; - /** @var array $resource */ $resource = json_decode($result['values'], true); - do { - try { - $this->logger->notice('Processing product', [ - 'code' => $resource['code'] ?? $resource['identifier'] ?? 'unknown', - ]); - - $this->handleProductModel($resource); - $isSuccess = true; - } catch (ORMInvalidArgumentException $ormInvalidArgumentException) { - ++$this->retryCount; - usleep($this->retryWaitTime); - - $this->logger->error('Retrying import', [ - 'product' => $result, - 'retry_count' => $this->retryCount, - 'error' => $ormInvalidArgumentException->getMessage(), - ]); - - $this->entityManager = $this->getNewEntityManager(); - } catch (\Throwable $throwable) { - ++$this->retryCount; - usleep($this->retryWaitTime); - - $this->logger->error('Error importing product', [ - 'message' => $throwable->getMessage(), - 'trace' => $throwable->getTraceAsString(), - ]); - - $this->entityManager = $this->getNewEntityManager(); - } - } while (false === $isSuccess && $this->retryCount < $this->maxRetryCount); - - unset($resource); - $this->removeEntry($payload, (int) $result['id']); - $this->retryCount = 0; + try { + $this->resourceProcessor->process($resource); + unset($resource); + $this->removeEntry($payload, (int) $result['id']); + } catch (MaxResourceProcessorRetryException) { + // Skip the failing line + continue; + } } } return $payload; } - - private function handleProductModel(array $resource): void - { - $this->handleProductGroup($resource); - $this->dispatcher->dispatch(new BeforeProcessingProductEvent($resource)); - - if (!$this->isProductProcessableChecker->check($resource)) { - return; - } - - $product = $this->process($resource); - $this->dispatcher->dispatch(new AfterProcessingProductEvent($resource, $product)); - $this->entityManager->flush(); - } - - private function handleProductGroup(array $resource): void - { - try { - $this->productGroupProcessor->process($resource); - $this->entityManager->flush(); - } catch (ORMInvalidArgumentException $ormInvalidArgumentException) { - if (!$this->entityManager->isOpen()) { - $this->logger->warning('Recreating entity manager', ['exception' => $ormInvalidArgumentException]); - $this->entityManager = $this->getNewEntityManager(); - } - - ++$this->retryCount; - - throw $ormInvalidArgumentException; - } - } - - private function process(array &$resource): ProductInterface - { - $product = $this->productRepository->findOneByCode($resource['code']); - - if (!$product instanceof ProductInterface) { - /** @var ProductInterface $product */ - $product = $this->productFactory->createNew(); - $product->setCode($resource['code']); - - $this->entityManager->persist($product); - $this->productProcessorChain->chain($product, $resource); - - $this->logger->info(Messages::hasBeenCreated($this->type, (string) $product->getCode())); - - return $product; - } - - $this->productProcessorChain->chain($product, $resource); - $this->logger->info(Messages::hasBeenUpdated($this->type, (string) $resource['code'])); - - return $product; - } - - private function getNewEntityManager(): EntityManagerInterface - { - $objectManager = $this->managerRegistry->resetManager(); - - if (!$objectManager instanceof EntityManagerInterface) { - throw new \LogicException('Wrong ObjectManager'); - } - - return $objectManager; - } } diff --git a/src/Task/ProductModel/ProcessProductModelsTask.php b/src/Task/ProductModel/ProcessProductModelsTask.php index 39789552..9fecacaa 100644 --- a/src/Task/ProductModel/ProcessProductModelsTask.php +++ b/src/Task/ProductModel/ProcessProductModelsTask.php @@ -4,33 +4,34 @@ namespace Synolia\SyliusAkeneoPlugin\Task\ProductModel; -use Doctrine\ORM\EntityManagerInterface; 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\Manager\ProcessManagerInterface; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload; use Synolia\SyliusAkeneoPlugin\Provider\Configuration\Api\ApiConnectionProviderInterface; -use Synolia\SyliusAkeneoPlugin\Task\AbstractProcessTask; +use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; +use Synolia\SyliusAkeneoPlugin\Task\AkeneoTaskInterface; +use Synolia\SyliusAkeneoPlugin\Task\TaskHandlerTrait; use Throwable; -final class ProcessProductModelsTask extends AbstractProcessTask +final class ProcessProductModelsTask implements AkeneoTaskInterface { + use TaskHandlerTrait{ + TaskHandlerTrait::__construct as private __taskHandlerConstruct; + } + public function __construct( - EntityManagerInterface $entityManager, - LoggerInterface $akeneoLogger, - ProcessManagerInterface $processManager, - BatchProductModelTask $task, private ProductFilter $productFilter, private ApiConnectionProviderInterface $apiConnectionProvider, private EventDispatcherInterface $eventDispatcher, - string $projectDir, + private LoggerInterface $logger, + private TaskHandlerProviderInterface $taskHandlerProvider, ) { - parent::__construct($entityManager, $processManager, $task, $akeneoLogger, $projectDir); + $this->__taskHandlerConstruct($taskHandlerProvider); } /** @@ -43,7 +44,7 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $this->logger->debug(self::class); if ($payload->isContinue()) { - $this->process($payload); + $this->continue($payload); return $payload; } @@ -71,15 +72,6 @@ public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInte $this->handle($payload, $resources); - $this->processManager->startAll(); - return $payload; } - - protected function createBatchPayload(PipelinePayloadInterface $payload): PipelinePayloadInterface - { - $commandContext = ($payload->hasCommandContext()) ? $payload->getCommandContext() : null; - - return new ProductModelPayload($payload->getAkeneoPimClient(), $commandContext); - } } diff --git a/src/Task/SetupTask.php b/src/Task/SetupTask.php index ebda9f24..3cf4b785 100644 --- a/src/Task/SetupTask.php +++ b/src/Task/SetupTask.php @@ -4,37 +4,18 @@ namespace Synolia\SyliusAkeneoPlugin\Task; -use Doctrine\ORM\EntityManagerInterface; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; final class SetupTask implements AkeneoTaskInterface { - public function __construct(private EntityManagerInterface $entityManager, private TearDownTask $tearDownTask) - { + public function __construct( + private TaskHandlerProviderInterface $taskHandlerProvider, + ) { } public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface { - if ($payload->isContinue()) { - $schemaManager = $this->entityManager->getConnection()->getSchemaManager(); - $tableExist = $schemaManager->tablesExist([$payload->getTmpTableName()]); - - if (true === $tableExist) { - return $payload; - } - } - - $this->tearDownTask->__invoke($payload); - - $query = sprintf( - 'CREATE TABLE `%s` ( - `id` INT NOT NULL AUTO_INCREMENT, - `values` JSON NULL, - PRIMARY KEY (`id`));', - $payload->getTmpTableName(), - ); - $this->entityManager->getConnection()->executeStatement($query); - - return $payload; + return $this->taskHandlerProvider->provide($payload)->setUp($payload); } } diff --git a/src/Task/TaskHandlerTrait.php b/src/Task/TaskHandlerTrait.php new file mode 100644 index 00000000..1a5d2d54 --- /dev/null +++ b/src/Task/TaskHandlerTrait.php @@ -0,0 +1,30 @@ +taskHandlerProvider->provide($pipelinePayload)->continue($pipelinePayload); + } + + protected function handle( + PipelinePayloadInterface $pipelinePayload, + ResourceCursorInterface|PageInterface $handleType, + ): void { + $this->taskHandlerProvider->provide($pipelinePayload)->handle($pipelinePayload, $handleType); + } +} diff --git a/src/Task/TearDownTask.php b/src/Task/TearDownTask.php index 56b06196..0bef4d41 100644 --- a/src/Task/TearDownTask.php +++ b/src/Task/TearDownTask.php @@ -4,33 +4,18 @@ namespace Synolia\SyliusAkeneoPlugin\Task; -use Doctrine\DBAL\Exception\ConnectionLost; -use Doctrine\ORM\EntityManagerInterface; use Synolia\SyliusAkeneoPlugin\Payload\PipelinePayloadInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Handler\Task\TaskHandlerProviderInterface; final class TearDownTask implements AkeneoTaskInterface { - public function __construct(private EntityManagerInterface $entityManager) - { + public function __construct( + private TaskHandlerProviderInterface $taskHandlerProvider, + ) { } public function __invoke(PipelinePayloadInterface $payload): PipelinePayloadInterface { - try { - $this->delete($payload); - } catch (ConnectionLost) { - $this->delete($payload); - } - - return $payload; - } - - private function delete(PipelinePayloadInterface $payload): void - { - $exists = $this->entityManager->getConnection()->getSchemaManager()->tablesExist([$payload->getTmpTableName()]); - - if ($exists) { - $this->entityManager->getConnection()->getSchemaManager()->dropTable($payload->getTmpTableName()); - } + return $this->taskHandlerProvider->provide($payload)->tearDown($payload); } } diff --git a/tests/PHPUnit/Provider/AkeneoAttributeDataProviderTest.php b/tests/PHPUnit/Provider/AkeneoAttributeDataProviderTest.php index 4c80d025..1e285f89 100644 --- a/tests/PHPUnit/Provider/AkeneoAttributeDataProviderTest.php +++ b/tests/PHPUnit/Provider/AkeneoAttributeDataProviderTest.php @@ -8,9 +8,9 @@ use donatj\MockWebServer\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse; use Synolia\SyliusAkeneoPlugin\Builder\Attribute\ProductAttributeValueValueBuilder; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProvider; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProviderInterface; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProviderInterface; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\SyliusAkeneoLocaleCodeProvider; use Tests\Synolia\SyliusAkeneoPlugin\PHPUnit\Task\Attribute\AbstractTaskTest; @@ -24,7 +24,7 @@ final class AkeneoAttributeDataProviderTest extends AbstractTaskTest private const DEFAULT_SCOPE = 'ecommerce'; /** @var AkeneoAttributeDataProviderInterface */ - private \Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributeDataProvider $attributeDataProvider; + private \Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributeDataProvider $attributeDataProvider; protected function setUp(): void { diff --git a/tests/PHPUnit/Provider/AkeneoAttributePropertiesProviderTest.php b/tests/PHPUnit/Provider/AkeneoAttributePropertiesProviderTest.php index aeeb0ed6..83ab65b2 100644 --- a/tests/PHPUnit/Provider/AkeneoAttributePropertiesProviderTest.php +++ b/tests/PHPUnit/Provider/AkeneoAttributePropertiesProviderTest.php @@ -7,7 +7,7 @@ use Akeneo\Pim\ApiClient\Api\AttributeApi; use donatj\MockWebServer\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Tests\Synolia\SyliusAkeneoPlugin\PHPUnit\Task\Attribute\AbstractTaskTest; /** @@ -17,7 +17,7 @@ */ final class AkeneoAttributePropertiesProviderTest extends AbstractTaskTest { - private \Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider $attributePropertiesProvider; + private \Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider $attributePropertiesProvider; protected function setUp(): void { diff --git a/tests/PHPUnit/Task/Association/AssociateProductsTaskTest.php b/tests/PHPUnit/Task/Association/AssociateProductsTaskTest.php index d4e47b90..0125dc39 100644 --- a/tests/PHPUnit/Task/Association/AssociateProductsTaskTest.php +++ b/tests/PHPUnit/Task/Association/AssociateProductsTaskTest.php @@ -24,7 +24,7 @@ use Synolia\SyliusAkeneoPlugin\Payload\Association\AssociationTypePayload; use Synolia\SyliusAkeneoPlugin\Payload\Category\CategoryPayload; use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider; use Synolia\SyliusAkeneoPlugin\Task\Association\AssociateProductsTask; use Synolia\SyliusAkeneoPlugin\Task\ProductModel\ProcessProductModelsTask; diff --git a/tests/PHPUnit/Task/Product/CreateConfigurableProductEntitiesTaskTest.php b/tests/PHPUnit/Task/Product/CreateConfigurableProductEntitiesTaskTest.php index 656551c0..dc8b8d0b 100644 --- a/tests/PHPUnit/Task/Product/CreateConfigurableProductEntitiesTaskTest.php +++ b/tests/PHPUnit/Task/Product/CreateConfigurableProductEntitiesTaskTest.php @@ -18,7 +18,7 @@ use Synolia\SyliusAkeneoPlugin\Payload\Category\CategoryPayload; use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload; use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider; use Synolia\SyliusAkeneoPlugin\Task\Product\ProcessProductsTask; use Synolia\SyliusAkeneoPlugin\Task\Product\SetupProductTask; diff --git a/tests/PHPUnit/Task/Product/CreateSimpleProductEntitiesTaskTest.php b/tests/PHPUnit/Task/Product/CreateSimpleProductEntitiesTaskTest.php index 744f851d..978a091f 100644 --- a/tests/PHPUnit/Task/Product/CreateSimpleProductEntitiesTaskTest.php +++ b/tests/PHPUnit/Task/Product/CreateSimpleProductEntitiesTaskTest.php @@ -17,7 +17,7 @@ use Sylius\Component\Core\Model\TaxonInterface; use Symfony\Component\HttpFoundation\Response as HttpResponse; use Synolia\SyliusAkeneoPlugin\Payload\Product\ProductPayload; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider; use Synolia\SyliusAkeneoPlugin\Repository\ProductAttributeRepository; use Synolia\SyliusAkeneoPlugin\Task\Product\ProcessProductsTask; diff --git a/tests/PHPUnit/Task/ProductGroup/ProcessProductGroupModelTaskTest.php b/tests/PHPUnit/Task/ProductGroup/ProcessProductGroupModelTaskTest.php index 26afeb6b..5de2d11e 100644 --- a/tests/PHPUnit/Task/ProductGroup/ProcessProductGroupModelTaskTest.php +++ b/tests/PHPUnit/Task/ProductGroup/ProcessProductGroupModelTaskTest.php @@ -14,7 +14,7 @@ use Synolia\SyliusAkeneoPlugin\Entity\ProductConfiguration; use Synolia\SyliusAkeneoPlugin\Entity\ProductGroup; use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider; use Synolia\SyliusAkeneoPlugin\Task\ProductGroup\ProcessProductGroupModelTask; use Synolia\SyliusAkeneoPlugin\Task\ProductModel\ProcessProductModelsTask; diff --git a/tests/PHPUnit/Task/ProductModel/AddOrUpdateProductModelTaskTest.php b/tests/PHPUnit/Task/ProductModel/AddOrUpdateProductModelTaskTest.php index 2740adf5..0c708acc 100644 --- a/tests/PHPUnit/Task/ProductModel/AddOrUpdateProductModelTaskTest.php +++ b/tests/PHPUnit/Task/ProductModel/AddOrUpdateProductModelTaskTest.php @@ -15,7 +15,7 @@ use Synolia\SyliusAkeneoPlugin\Entity\ProductConfigurationImageMapping; use Synolia\SyliusAkeneoPlugin\Entity\ProductGroup; use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider; use Synolia\SyliusAkeneoPlugin\Repository\ProductGroupRepository; use Synolia\SyliusAkeneoPlugin\Task\ProductModel\ProcessProductModelsTask; diff --git a/tests/PHPUnit/Task/ProductModel/EnableDisableProductModelTaskTest.php b/tests/PHPUnit/Task/ProductModel/EnableDisableProductModelTaskTest.php index 2e267f73..6c99941e 100644 --- a/tests/PHPUnit/Task/ProductModel/EnableDisableProductModelTaskTest.php +++ b/tests/PHPUnit/Task/ProductModel/EnableDisableProductModelTaskTest.php @@ -9,7 +9,7 @@ use Sylius\Component\Core\Model\Product; use Symfony\Component\HttpFoundation\Response as HttpResponse; use Synolia\SyliusAkeneoPlugin\Payload\ProductModel\ProductModelPayload; -use Synolia\SyliusAkeneoPlugin\Provider\AkeneoAttributePropertiesProvider; +use Synolia\SyliusAkeneoPlugin\Provider\Data\AkeneoAttributePropertiesProvider; use Synolia\SyliusAkeneoPlugin\Provider\TaskProvider; use Synolia\SyliusAkeneoPlugin\Task\ProductModel\ProcessProductModelsTask; use Synolia\SyliusAkeneoPlugin\Task\SetupTask; @@ -22,8 +22,7 @@ */ final class EnableDisableProductModelTaskTest extends AbstractTaskTest { - /** @var AkeneoTaskProvider */ - private $taskProvider; + private TaskProvider $taskProvider; protected function setUp(): void { diff --git a/tests/PHPUnit/Task/ProductOption/DeleteTaskTest.php b/tests/PHPUnit/Task/ProductOption/DeleteTaskTest.php deleted file mode 100644 index 320353bb..00000000 --- a/tests/PHPUnit/Task/ProductOption/DeleteTaskTest.php +++ /dev/null @@ -1,50 +0,0 @@ -taskProvider = $this->getContainer()->get(TaskProvider::class); - } - - public function testDeleteOptionTask(): void - { - $optionsPayload = new OptionsPayload($this->createClient()); - - /** @var \Sylius\Component\Resource\Factory\FactoryInterface $optionFactory */ - $optionFactory = $this->getContainer()->get('sylius.factory.product_option'); - /** @var ProductOption $option */ - $option = $optionFactory->createNew(); - $option->setCode('fakeCode'); - $option->setName('fakeName'); - $this->manager->persist($option); - $this->manager->flush(); - - /** @var \Synolia\SyliusAkeneoPlugin\Task\Option\DeleteTask $deleteTask */ - $deleteTask = $this->taskProvider->get(DeleteTask::class); - $deleteTask->__invoke($optionsPayload); - - $productOptionRepository = $this->getContainer()->get('sylius.repository.product_option'); - /** @var \Sylius\Component\Product\Model\ProductOptionInterface $productOption */ - $productOption = $productOptionRepository->findOneBy(['code' => 'fakeCode']); - $this->assertNull($productOption); - } -}