From 4da9a3e3d4392fc75fd62df3774b059c7c455f84 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Fri, 5 Jul 2024 17:44:25 +0200 Subject: [PATCH] Fix for no more existing values for some locales --- .../AttributeValueHandlerSpec.php | 133 +++++++++++------- src/ValueHandler/AttributeValueHandler.php | 8 ++ 2 files changed, 93 insertions(+), 48 deletions(-) diff --git a/spec/ValueHandler/AttributeValueHandlerSpec.php b/spec/ValueHandler/AttributeValueHandlerSpec.php index 7e7d3a95..36fab1f2 100644 --- a/spec/ValueHandler/AttributeValueHandlerSpec.php +++ b/spec/ValueHandler/AttributeValueHandlerSpec.php @@ -17,6 +17,7 @@ use Sylius\Component\Resource\Repository\RepositoryInterface; use Sylius\Component\Resource\Translation\Provider\TranslationLocaleProviderInterface; use Webgriffe\SyliusAkeneoPlugin\Converter\ValueConverterInterface; +use Webgriffe\SyliusAkeneoPlugin\ValueHandler\AttributeValueHandler; use Webgriffe\SyliusAkeneoPlugin\ValueHandlerInterface; use Webmozart\Assert\InvalidArgumentException; @@ -38,7 +39,7 @@ class AttributeValueHandlerSpec extends ObjectBehavior private const PRODUCT_OPTION_CODE = 'finitura'; - function let( + public function let( ProductAttributeInterface $checkboxProductAttribute, ProductAttributeInterface $textProductAttribute, ProductAttributeInterface $textareaProductAttribute, @@ -53,7 +54,7 @@ function let( ProductInterface $product, ProductOptionInterface $productOption, ValueConverterInterface $valueConverter - ) { + ): void { $checkboxProductAttribute->getCode()->willReturn(self::CHECKBOX_ATTRIBUTE_CODE); $textProductAttribute->getCode()->willReturn(self::TEXT_ATTRIBUTE_CODE); $textareaProductAttribute->getCode()->willReturn(self::TEXTAREA_ATTRIBUTE_CODE); @@ -98,67 +99,67 @@ function let( $this->beConstructedWith($attributeRepository, $factory, $localeProvider, $valueConverter); } - function it_is_initializable() + public function it_is_initializable(): void { - $this->shouldHaveType(\Webgriffe\SyliusAkeneoPlugin\ValueHandler\AttributeValueHandler::class); + $this->shouldHaveType(AttributeValueHandler::class); } - function it_implements_value_handler_interface() + public function it_implements_value_handler_interface(): void { $this->shouldHaveType(ValueHandlerInterface::class); } - function it_supports_product_variant_as_subject(ProductVariantInterface $productVariant) + function it_supports_product_variant_as_subject(ProductVariantInterface $productVariant): void { $this->supports($productVariant, self::TEXT_ATTRIBUTE_CODE, [])->shouldReturn(true); } - function it_does_not_support_other_type_of_subject() + public function it_does_not_support_other_type_of_subject(): void { $this->supports(new \stdClass(), self::TEXT_ATTRIBUTE_CODE, [])->shouldReturn(false); } - function it_support_existing_text_attributes(ProductVariantInterface $productVariant) + public function it_support_existing_text_attributes(ProductVariantInterface $productVariant): void { $this->supports($productVariant, self::TEXT_ATTRIBUTE_CODE, [])->shouldReturn(true); } - function it_support_existing_textarea_attributes(ProductVariantInterface $productVariant) + public function it_support_existing_textarea_attributes(ProductVariantInterface $productVariant): void { $this->supports($productVariant, self::TEXTAREA_ATTRIBUTE_CODE, [])->shouldReturn(true); } - function it_support_existing_checkbox_attributes(ProductVariantInterface $productVariant) + public function it_support_existing_checkbox_attributes(ProductVariantInterface $productVariant): void { $this->supports($productVariant, self::CHECKBOX_ATTRIBUTE_CODE, [])->shouldReturn(true); } - function it_support_existing_select_attributes(ProductVariantInterface $productVariant) + public function it_support_existing_select_attributes(ProductVariantInterface $productVariant): void { $this->supports($productVariant, self::SELECT_ATTRIBUTE_CODE, [])->shouldReturn(true); } - function it_support_existing_integer_attributes(ProductVariantInterface $productVariant) + public function it_support_existing_integer_attributes(ProductVariantInterface $productVariant): void { $this->supports($productVariant, self::INTEGER_ATTRIBUTE_CODE, [])->shouldReturn(true); } - function it_does_not_support_existing_attributes_of_not_supported_type(ProductVariantInterface $productVariant) + public function it_does_not_support_existing_attributes_of_not_supported_type(ProductVariantInterface $productVariant): void { $this->supports($productVariant, self::DATETIME_ATTRIBUTE_CODE, [])->shouldReturn(false); } - function it_does_not_support_not_existing_attribute(ProductVariantInterface $productVariant) + public function it_does_not_support_not_existing_attribute(ProductVariantInterface $productVariant): void { $this->supports($productVariant, self::NOT_EXISTING_ATTRIBUTE_CODE, [])->shouldReturn(false); } - function it_does_not_support_attributes_that_are_product_options(ProductVariantInterface $productVariant) + public function it_does_not_support_attributes_that_are_product_options(ProductVariantInterface $productVariant): void { $this->supports($productVariant, self::PRODUCT_OPTION_CODE, [])->shouldReturn(false); } - function it_throws_exception_during_handle_when_subject_is_not_product_variant() + public function it_throws_exception_during_handle_when_subject_is_not_product_variant(): void { $this ->shouldThrow( @@ -173,7 +174,7 @@ function it_throws_exception_during_handle_when_subject_is_not_product_variant() ->during('handle', [new \stdClass(), self::TEXT_ATTRIBUTE_CODE, []]); } - function it_throws_exception_during_handle_when_product_variant_hasnt_an_associated_product(ProductVariantInterface $productVariant) + public function it_throws_exception_during_handle_when_product_variant_hasnt_an_associated_product(ProductVariantInterface $productVariant): void { $productVariant->getProduct()->willReturn(null); $this @@ -181,7 +182,7 @@ function it_throws_exception_during_handle_when_product_variant_hasnt_an_associa ->during('handle', [$productVariant, self::TEXT_ATTRIBUTE_CODE, []]); } - function it_throws_exception_during_handle_when_attribute_is_not_found(ProductVariantInterface $productVariant) + public function it_throws_exception_during_handle_when_attribute_is_not_found(ProductVariantInterface $productVariant): void { $this ->shouldThrow( @@ -193,7 +194,7 @@ function it_throws_exception_during_handle_when_attribute_is_not_found(ProductVa ->during('handle', [$productVariant, self::NOT_EXISTING_ATTRIBUTE_CODE, []]); } - function it_creates_text_product_attribute_value_from_factory_with_all_locales_if_it_does_not_already_exist( + public function it_creates_text_product_attribute_value_from_factory_with_all_locales_if_it_does_not_already_exist( ProductVariantInterface $productVariant, ProductAttributeValueInterface $itAttributeValue, ProductAttributeValueInterface $enAttributeValue, @@ -202,7 +203,7 @@ function it_creates_text_product_attribute_value_from_factory_with_all_locales_i ProductInterface $product, ValueConverterInterface $valueConverter, ProductAttributeInterface $textProductAttribute - ) { + ): void { $factory->createNew()->willReturn($enAttributeValue, $itAttributeValue, $deAttributeValue); $product->getAttributeByCodeAndLocale(Argument::type('string'), Argument::type('string'))->willReturn(null); @@ -230,7 +231,7 @@ function it_creates_text_product_attribute_value_from_factory_with_all_locales_i $deAttributeValue->setValue('Agape')->shouldHaveBeenCalled(); } - function it_creates_text_product_attribute_value_from_factory_with_the_given_locale_if_it_does_not_already_exist( + public function it_creates_text_product_attribute_value_from_factory_with_the_given_locale_if_it_does_not_already_exist( ProductVariantInterface $productVariant, ProductAttributeValueInterface $itAttributeValue, ProductAttributeValueInterface $enAttributeValue, @@ -239,7 +240,7 @@ function it_creates_text_product_attribute_value_from_factory_with_the_given_loc ProductInterface $product, ValueConverterInterface $valueConverter, ProductAttributeInterface $textProductAttribute - ) { + ): void { $factory->createNew()->willReturn($enAttributeValue, $itAttributeValue); $product->getAttributeByCodeAndLocale(Argument::type('string'), Argument::type('string'))->willReturn(null); @@ -271,7 +272,7 @@ function it_creates_text_product_attribute_value_from_factory_with_the_given_loc $deAttributeValue->setValue('Holz')->shouldNotHaveBeenCalled(); } - function it_creates_checkbox_product_attribute_value_from_factory_with_all_locales_if_it_does_not_already_exist( + public function it_creates_checkbox_product_attribute_value_from_factory_with_all_locales_if_it_does_not_already_exist( ProductVariantInterface $productVariant, ProductAttributeValueInterface $itAttributeValue, ProductAttributeValueInterface $enAttributeValue, @@ -280,7 +281,7 @@ function it_creates_checkbox_product_attribute_value_from_factory_with_all_local ProductInterface $product, ValueConverterInterface $valueConverter, ProductAttributeInterface $checkboxProductAttribute - ) { + ): void { $factory->createNew()->willReturn($enAttributeValue, $itAttributeValue, $deAttributeValue); $product->getAttributeByCodeAndLocale(Argument::type('string'), Argument::type('string'))->willReturn(null); @@ -309,7 +310,7 @@ function it_creates_checkbox_product_attribute_value_from_factory_with_all_local $deAttributeValue->setValue(true)->shouldHaveBeenCalled(); } - function it_creates_checkbox_product_attribute_value_from_factory_with_the_given_locale_if_it_does_not_already_exist( + public function it_creates_checkbox_product_attribute_value_from_factory_with_the_given_locale_if_it_does_not_already_exist( ProductVariantInterface $productVariant, ProductAttributeValueInterface $itAttributeValue, ProductAttributeValueInterface $enAttributeValue, @@ -318,7 +319,7 @@ function it_creates_checkbox_product_attribute_value_from_factory_with_the_given ProductInterface $product, ValueConverterInterface $valueConverter, ProductAttributeInterface $checkboxProductAttribute - ) { + ): void { $factory->createNew()->willReturn($enAttributeValue, $itAttributeValue); $product->getAttributeByCodeAndLocale(Argument::type('string'), Argument::type('string'))->willReturn(null); @@ -350,7 +351,7 @@ function it_creates_checkbox_product_attribute_value_from_factory_with_the_given $deAttributeValue->setValue(false)->shouldNotHaveBeenCalled(); } - function it_creates_select_product_attribute_value_with_the_given_locale_if_it_does_not_already_exist( + public function it_creates_select_product_attribute_value_with_the_given_locale_if_it_does_not_already_exist( ProductVariantInterface $productVariant, ProductInterface $product, ProductAttributeValueInterface $enAttributeValue, @@ -358,7 +359,7 @@ function it_creates_select_product_attribute_value_with_the_given_locale_if_it_d FactoryInterface $factory, ValueConverterInterface $valueConverter, ProductAttributeInterface $selectProductAttribute - ) { + ): void { $factory->createNew()->willReturn($enAttributeValue, $itAttributeValue); $product->getAttributeByCodeAndLocale(Argument::type('string'), Argument::type('string'))->willReturn(null); $value = [ @@ -387,7 +388,7 @@ function it_creates_select_product_attribute_value_with_the_given_locale_if_it_d $itAttributeValue->setValue('brand_agape_IT')->shouldHaveBeenCalled(); } - function it_creates_select_product_attribute_value_with_all_locales_if_it_does_not_already_exist( + public function it_creates_select_product_attribute_value_with_all_locales_if_it_does_not_already_exist( ProductVariantInterface $productVariant, ProductInterface $product, ProductAttributeValueInterface $enAttributeValue, @@ -396,7 +397,7 @@ function it_creates_select_product_attribute_value_with_all_locales_if_it_does_n FactoryInterface $factory, ValueConverterInterface $valueConverter, ProductAttributeInterface $selectProductAttribute - ) { + ): void { $factory->createNew()->willReturn($enAttributeValue, $itAttributeValue, $deAttributeValue); $product->getAttributeByCodeAndLocale(Argument::type('string'), Argument::type('string'))->willReturn(null); $value = [ @@ -424,7 +425,7 @@ function it_creates_select_product_attribute_value_with_all_locales_if_it_does_n $product->addAttribute($deAttributeValue)->shouldHaveBeenCalled(); } - function it_creates_select_product_attribute_value_with_all_options_and_the_given_locale_if_it_does_not_already_exist( + public function it_creates_select_product_attribute_value_with_all_options_and_the_given_locale_if_it_does_not_already_exist( ProductVariantInterface $productVariant, ProductInterface $product, ProductAttributeValueInterface $enAttributeValue, @@ -432,7 +433,7 @@ function it_creates_select_product_attribute_value_with_all_options_and_the_give FactoryInterface $factory, ValueConverterInterface $valueConverter, ProductAttributeInterface $selectProductAttribute - ) { + ): void { $factory->createNew()->willReturn($enAttributeValue, $itAttributeValue); $product->getAttributeByCodeAndLocale(Argument::type('string'), Argument::type('string'))->willReturn(null); $value = [ @@ -460,7 +461,7 @@ function it_creates_select_product_attribute_value_with_all_options_and_the_give $itAttributeValue->setValue(['brand_agape_IT', 'brand_agape', 'brand_agape_plus'])->shouldHaveBeenCalled(); } - function it_creates_select_product_attribute_value_with_all_options_and_all_locales_if_it_does_not_already_exist( + public function it_creates_select_product_attribute_value_with_all_options_and_all_locales_if_it_does_not_already_exist( ProductVariantInterface $productVariant, ProductInterface $product, ProductAttributeValueInterface $enAttributeValue, @@ -469,7 +470,7 @@ function it_creates_select_product_attribute_value_with_all_options_and_all_loca FactoryInterface $factory, ValueConverterInterface $valueConverter, ProductAttributeInterface $selectProductAttribute - ) { + ): void { $factory->createNew()->willReturn($enAttributeValue, $itAttributeValue, $deAttributeValue); $product->getAttributeByCodeAndLocale(Argument::type('string'), Argument::type('string'))->willReturn(null); $value = [ @@ -496,7 +497,7 @@ function it_creates_select_product_attribute_value_with_all_options_and_all_loca $product->addAttribute($deAttributeValue)->shouldHaveBeenCalled(); } - function it_creates_integer_product_attribute_value_with_all_locales_if_it_does_not_already_exist( + public function it_creates_integer_product_attribute_value_with_all_locales_if_it_does_not_already_exist( ProductVariantInterface $productVariant, ProductInterface $product, ProductAttributeValueInterface $enAttributeValue, @@ -505,7 +506,7 @@ function it_creates_integer_product_attribute_value_with_all_locales_if_it_does_ FactoryInterface $factory, ValueConverterInterface $valueConverter, ProductAttributeInterface $integerProductAttribute - ) { + ): void { $factory->createNew()->willReturn($enAttributeValue, $itAttributeValue, $deAttributeValue); $product->getAttributeByCodeAndLocale(Argument::type('string'), Argument::type('string'))->willReturn(null); $value = [ @@ -533,16 +534,18 @@ function it_creates_integer_product_attribute_value_with_all_locales_if_it_does_ $product->addAttribute($deAttributeValue)->shouldHaveBeenCalled(); } - function it_does_not_create_the_same_attribute_value_more_than_once( + public function it_does_not_create_the_same_attribute_value_more_than_once( ProductVariantInterface $productVariant, ProductAttributeValueInterface $itAttributeValue, FactoryInterface $factory, ProductInterface $product, ProductAttributeInterface $textProductAttribute, ValueConverterInterface $valueConverter - ) { + ): void { $factory->createNew()->willReturn($itAttributeValue); $product->getAttributeByCodeAndLocale(self::TEXT_ATTRIBUTE_CODE, 'it_IT')->willReturn(null, $itAttributeValue); + $product->getAttributeByCodeAndLocale(self::TEXT_ATTRIBUTE_CODE, 'en_US')->willReturn(null, null); + $product->getAttributeByCodeAndLocale(self::TEXT_ATTRIBUTE_CODE, 'de_DE')->willReturn(null, null); $firstValue = [ [ @@ -573,12 +576,12 @@ function it_does_not_create_the_same_attribute_value_more_than_once( $itAttributeValue->setValue('Agape Plus')->shouldHaveBeenCalledOnce(); } - function it_removes_existing_product_attribute_value_if_value_is_null( + public function it_removes_existing_product_attribute_value_if_value_is_null( ProductVariantInterface $productVariant, ProductInterface $product, ProductAttributeValueInterface $itAttributeValue, ProductAttributeValueInterface $enAttributeValue - ) { + ): void { $product->getAttributeByCodeAndLocale(self::TEXT_ATTRIBUTE_CODE, 'it_IT')->willReturn($itAttributeValue); $product->getAttributeByCodeAndLocale(self::TEXT_ATTRIBUTE_CODE, 'en_US')->willReturn($enAttributeValue); $product->getAttributeByCodeAndLocale(self::TEXT_ATTRIBUTE_CODE, 'de_DE')->willReturn(null); @@ -597,16 +600,20 @@ function it_removes_existing_product_attribute_value_if_value_is_null( $product->addAttribute(Argument::any())->shouldNotHaveBeenCalled(); } - function it_skips_locales_not_specified_in_sylius( + public function it_skips_locales_not_specified_in_sylius( ProductVariantInterface $productVariant, ProductInterface $product - ) { + ): void { + $product->getAttributeByCodeAndLocale(self::TEXT_ATTRIBUTE_CODE, 'en_US')->willReturn(null); + $product->getAttributeByCodeAndLocale(self::TEXT_ATTRIBUTE_CODE, 'it_IT')->willReturn(null); + $product->getAttributeByCodeAndLocale(self::TEXT_ATTRIBUTE_CODE, 'de_DE')->willReturn(null); + $this->handle($productVariant, self::TEXT_ATTRIBUTE_CODE, [['locale' => 'es_ES', 'scope' => null, 'data' => 'New value']]); $product->addAttribute(Argument::type(ProductAttributeValueInterface::class))->shouldNotHaveBeenCalled(); } - function it_skips_values_related_to_channels_that_are_not_associated_to_the_product( + public function it_skips_values_related_to_channels_that_are_not_associated_to_the_product( ProductVariantInterface $productVariant, ProductAttributeValueInterface $itAttributeValue, ProductAttributeValueInterface $enAttributeValue, @@ -614,7 +621,7 @@ function it_skips_values_related_to_channels_that_are_not_associated_to_the_prod ProductInterface $product, ValueConverterInterface $valueConverter, ProductAttributeInterface $textProductAttribute - ) { + ): void { $factory->createNew()->willReturn($enAttributeValue, $itAttributeValue); $product->getAttributeByCodeAndLocale(Argument::type('string'), Argument::type('string'))->willReturn(null); @@ -656,15 +663,19 @@ function it_skips_values_related_to_channels_that_are_not_associated_to_the_prod $enAttributeValue->setValue('Woody')->shouldNotHaveBeenCalled(); } - public function it_throws_when_data_is_not_an_array(ProductVariantInterface $productVariant): void - { + public function it_throws_when_data_is_not_an_array( + ProductInterface $product, + ProductVariantInterface $productVariant, + ): void { $this ->shouldThrow(new \InvalidArgumentException('Invalid Akeneo value data: expected an array, "NULL" given.',)) ->during('handle', [$productVariant, self::TEXT_ATTRIBUTE_CODE, [null]]); } - public function it_throws_when_data_doesnt_contain_scope_info(ProductVariantInterface $productVariant): void - { + public function it_throws_when_data_doesnt_contain_scope_info( + ProductInterface $product, + ProductVariantInterface $productVariant, + ): void { $this ->shouldThrow(new \InvalidArgumentException('Invalid Akeneo value data: required "scope" information was not found.',)) ->during( @@ -681,4 +692,30 @@ public function it_throws_when_data_doesnt_contain_scope_info(ProductVariantInte ] ); } + + public function it_removes_no_more_existing_product_attribute_value_for_some_locale( + ProductVariantInterface $productVariant, + ProductInterface $product, + ProductAttributeValueInterface $itAttributeValue, + ProductAttributeValueInterface $enAttributeValue, + ValueConverterInterface $valueConverter, + ProductAttributeInterface $selectProductAttribute, + ): void { + $product->getAttributeByCodeAndLocale(self::SELECT_ATTRIBUTE_CODE, 'it_IT')->willReturn($itAttributeValue); + $product->getAttributeByCodeAndLocale(self::SELECT_ATTRIBUTE_CODE, 'en_US')->willReturn($enAttributeValue); + $product->getAttributeByCodeAndLocale(self::SELECT_ATTRIBUTE_CODE, 'de_DE')->willReturn(null); + $value = [ + [ + 'scope' => null, + 'locale' => 'en_US', + 'data' => 'Agape', + ], + ]; + $valueConverter->convert($selectProductAttribute, 'Agape', 'en_US')->willReturn('Agape'); + + $this->handle($productVariant, self::SELECT_ATTRIBUTE_CODE, $value); + + $product->removeAttribute($itAttributeValue)->shouldHaveBeenCalled(); + $product->addAttribute(Argument::any())->shouldHaveBeenCalled(); + } } diff --git a/src/ValueHandler/AttributeValueHandler.php b/src/ValueHandler/AttributeValueHandler.php index 52b7229c..0bf157e7 100644 --- a/src/ValueHandler/AttributeValueHandler.php +++ b/src/ValueHandler/AttributeValueHandler.php @@ -102,6 +102,7 @@ public function handle($subject, string $attributeCode, array $value): void $productChannelCodes = array_map(static fn (ChannelInterface $channel): ?string => $channel->getCode(), $product->getChannels()->toArray()); $productChannelCodes = array_filter($productChannelCodes); + $updatedLocales = []; foreach ($value as $valueData) { if (!is_array($valueData)) { throw new InvalidArgumentException(sprintf('Invalid Akeneo value data: expected an array, "%s" given.', gettype($valueData))); @@ -119,11 +120,18 @@ public function handle($subject, string $attributeCode, array $value): void if ($valueLocaleCode !== null) { $localeCodesToSet = in_array($valueLocaleCode, $availableLocalesCodes, true) ? [$valueLocaleCode] : []; } + $updatedLocales = array_merge($updatedLocales, $localeCodesToSet); foreach ($localeCodesToSet as $localeCode) { $this->handleAttributeValue($attribute, $valueData['data'], $localeCode, $product); } } + // Remove attribute values for locales that are no more present in the Akeneo data + foreach ($availableLocalesCodes as $availableLocaleCode) { + if (!in_array($availableLocaleCode, $updatedLocales, true)) { + $this->handleAttributeValue($attribute, null, $availableLocaleCode, $product); + } + } } /**