From fdf203caf572faa07c0120e5d320e8abe123a330 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 30 Sep 2024 13:01:16 +0200 Subject: [PATCH] Extract product option value normalizer --- config/services/serializer.php | 10 ++ ...ntTypeProductOptionValueNormalizeEvent.php | 39 +++++++ src/Serializer/ProductNormalizer.php | 3 +- .../ProductOptionValueNormalizer.php | 65 +++++++++++ src/Serializer/ProductVariantNormalizer.php | 33 ++---- .../Unit/Serializer/ProductNormalizerTest.php | 8 +- .../ProductOptionValueNormalizerTest.php | 101 ++++++++++++++++++ .../ProductVariantNormalizerTest.php | 10 +- 8 files changed, 240 insertions(+), 29 deletions(-) create mode 100644 src/Event/ProductDocumentType/ProductDocumentTypeProductOptionValueNormalizeEvent.php create mode 100644 src/Serializer/ProductOptionValueNormalizer.php create mode 100644 tests/Unit/Serializer/ProductOptionValueNormalizerTest.php diff --git a/config/services/serializer.php b/config/services/serializer.php index 8136016..224f8ba 100644 --- a/config/services/serializer.php +++ b/config/services/serializer.php @@ -5,6 +5,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Webgriffe\SyliusElasticsearchPlugin\Serializer\ProductNormalizer; +use Webgriffe\SyliusElasticsearchPlugin\Serializer\ProductOptionValueNormalizer; use Webgriffe\SyliusElasticsearchPlugin\Serializer\ProductTranslationNormalizer; use Webgriffe\SyliusElasticsearchPlugin\Serializer\ProductVariantNormalizer; @@ -23,6 +24,15 @@ ; $services->set('webgriffe.sylius_elasticsearch_plugin.serializer.product_variant_normalizer', ProductVariantNormalizer::class) + ->lazy() + ->args([ + service('event_dispatcher'), + service('serializer'), + ]) + ->tag('serializer.normalizer', ['priority' => 200]) + ; + + $services->set('webgriffe.sylius_elasticsearch_plugin.serializer.product_option_value_normalizer', ProductOptionValueNormalizer::class) ->args([ service('event_dispatcher'), ]) diff --git a/src/Event/ProductDocumentType/ProductDocumentTypeProductOptionValueNormalizeEvent.php b/src/Event/ProductDocumentType/ProductDocumentTypeProductOptionValueNormalizeEvent.php new file mode 100644 index 0000000..02a857d --- /dev/null +++ b/src/Event/ProductDocumentType/ProductDocumentTypeProductOptionValueNormalizeEvent.php @@ -0,0 +1,39 @@ +optionValue; + } + + public function getChannel(): ChannelInterface + { + return $this->channel; + } + + public function getNormalizedProductOptionValue(): array + { + return $this->normalizedProductOptionValue; + } + + public function setNormalizedProductOptionValue(array $normalizedProductOptionValue): void + { + $this->normalizedProductOptionValue = $normalizedProductOptionValue; + } +} diff --git a/src/Serializer/ProductNormalizer.php b/src/Serializer/ProductNormalizer.php index 10b41a4..1fb6328 100644 --- a/src/Serializer/ProductNormalizer.php +++ b/src/Serializer/ProductNormalizer.php @@ -25,7 +25,6 @@ use Sylius\Component\Product\Resolver\ProductVariantResolverInterface; use Sylius\Component\Taxonomy\Model\TaxonTranslationInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -use Symfony\Component\Serializer\SerializerInterface; use Webgriffe\SyliusElasticsearchPlugin\Event\ProductDocumentType\ProductDocumentTypeProductNormalizeEvent; use Webgriffe\SyliusElasticsearchPlugin\Model\FilterableInterface; use Webmozart\Assert\Assert; @@ -46,7 +45,7 @@ class ProductNormalizer implements NormalizerInterface public function __construct( private readonly ProductVariantResolverInterface $productVariantResolver, private readonly EventDispatcherInterface $eventDispatcher, - private readonly SerializerInterface&NormalizerInterface $serializer, + private readonly NormalizerInterface $serializer, private readonly string $systemDefaultLocaleCode, ) { } diff --git a/src/Serializer/ProductOptionValueNormalizer.php b/src/Serializer/ProductOptionValueNormalizer.php new file mode 100644 index 0000000..e61656a --- /dev/null +++ b/src/Serializer/ProductOptionValueNormalizer.php @@ -0,0 +1,65 @@ + $optionValue->getId(), + 'code' => $optionValue->getCode(), + 'value' => $optionValue->getValue(), + 'name' => [], + ]; + /** @var ProductOptionValueTranslationInterface $optionValueTranslation */ + foreach ($optionValue->getTranslations() as $optionValueTranslation) { + $localeCode = $optionValueTranslation->getLocale(); + Assert::string($localeCode); + $normalizedOptionValue['name'][] = [ + $localeCode => $optionValueTranslation->getValue(), + ]; + } + + $event = new ProductDocumentTypeProductOptionValueNormalizeEvent($optionValue, $channel, $normalizedOptionValue); + $this->eventDispatcher->dispatch($event); + + return $event->getNormalizedProductOptionValue(); + } + + public function getSupportedTypes(?string $format): array + { + return [ProductOptionValueInterface::class => true]; + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + return $data instanceof ProductOptionValueInterface && + array_key_exists('type', $context) && + $context['type'] === 'webgriffe_sylius_elasticsearch_plugin' + ; + } +} diff --git a/src/Serializer/ProductVariantNormalizer.php b/src/Serializer/ProductVariantNormalizer.php index 9c6d1e1..e06b03a 100644 --- a/src/Serializer/ProductVariantNormalizer.php +++ b/src/Serializer/ProductVariantNormalizer.php @@ -12,7 +12,6 @@ use Sylius\Component\Product\Model\ProductOptionInterface; use Sylius\Component\Product\Model\ProductOptionTranslationInterface; use Sylius\Component\Product\Model\ProductOptionValueInterface; -use Sylius\Component\Product\Model\ProductOptionValueTranslationInterface; use Sylius\Component\Product\Model\ProductVariantTranslationInterface; use Sylius\Component\Promotion\Model\CatalogPromotionTranslationInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -20,10 +19,14 @@ use Webgriffe\SyliusElasticsearchPlugin\Model\FilterableInterface; use Webmozart\Assert\Assert; -final class ProductVariantNormalizer implements NormalizerInterface +/** + * @final + */ +class ProductVariantNormalizer implements NormalizerInterface { public function __construct( private readonly EventDispatcherInterface $eventDispatcher, + private readonly NormalizerInterface $serializer, ) { } @@ -76,6 +79,8 @@ public function normalize(mixed $object, ?string $format = null, array $context $normalizedVariant['options'][] = $this->normalizeProductOptionAndProductOptionValue( $optionAndValue['option'], $optionAndValue['value'], + $format, + $context, ); } @@ -148,6 +153,8 @@ private function normalizeChannelPricing(?ChannelPricingInterface $channelPricin private function normalizeProductOptionAndProductOptionValue( ProductOptionInterface $option, ProductOptionValueInterface $optionValue, + ?string $format = null, + array $context = [], ): array { $filterable = false; if ($option instanceof FilterableInterface) { @@ -158,7 +165,7 @@ private function normalizeProductOptionAndProductOptionValue( 'code' => $option->getCode(), 'name' => [], 'filterable' => $filterable, - 'value' => $this->normalizeProductOptionValue($optionValue), + 'value' => $this->serializer->normalize($optionValue, $format, $context), ]; /** @var ProductOptionTranslationInterface $optionTranslation */ foreach ($option->getTranslations() as $optionTranslation) { @@ -171,24 +178,4 @@ private function normalizeProductOptionAndProductOptionValue( return $normalizedOption; } - - private function normalizeProductOptionValue(ProductOptionValueInterface $optionValue): array - { - $normalizedOptionValue = [ - 'sylius-id' => $optionValue->getId(), - 'code' => $optionValue->getCode(), - 'value' => $optionValue->getValue(), - 'name' => [], - ]; - /** @var ProductOptionValueTranslationInterface $optionValueTranslation */ - foreach ($optionValue->getTranslations() as $optionValueTranslation) { - $localeCode = $optionValueTranslation->getLocale(); - Assert::string($localeCode); - $normalizedOptionValue['name'][] = [ - $localeCode => $optionValueTranslation->getValue(), - ]; - } - - return $normalizedOptionValue; - } } diff --git a/tests/Unit/Serializer/ProductNormalizerTest.php b/tests/Unit/Serializer/ProductNormalizerTest.php index 69a3d41..343f55c 100644 --- a/tests/Unit/Serializer/ProductNormalizerTest.php +++ b/tests/Unit/Serializer/ProductNormalizerTest.php @@ -1,5 +1,7 @@ productNormalizer = new ProductNormalizer( new DefaultProductVariantResolver(), $eventDispatcher, - new Serializer([new ProductVariantNormalizer($eventDispatcher)]), + $serializer, 'en_US', ); @@ -79,7 +83,7 @@ public function testItDoesNotSupportNormalizationWithoutType(): void public function testItNormalizeProduct(): void { - $productNormalized = $this->productNormalizer->normalize($this->productToNormalize, null, ['channel' => $this->channel]); + $productNormalized = $this->productNormalizer->normalize($this->productToNormalize, null, ['channel' => $this->channel, 'type' => 'webgriffe_sylius_elasticsearch_plugin']); $this->assertIsArray($productNormalized); $this->assertEquals(1, $productNormalized['sylius-id']); diff --git a/tests/Unit/Serializer/ProductOptionValueNormalizerTest.php b/tests/Unit/Serializer/ProductOptionValueNormalizerTest.php new file mode 100644 index 0000000..27dabad --- /dev/null +++ b/tests/Unit/Serializer/ProductOptionValueNormalizerTest.php @@ -0,0 +1,101 @@ +productOptionValueNormalizer = new ProductOptionValueNormalizer( + new EventDispatcher(), + ); + + $this->productOptionValueToNormalize = new ProductOptionValue(); + + $reflectionProductOptionValue = new \ReflectionClass(ProductOptionValue::class); + $productOptionValueIdProperty = $reflectionProductOptionValue->getProperty('id'); + + $sizeProductOption = new ProductOption(); + + $productOptionValueIdProperty->setValue($this->productOptionValueToNormalize, 21); + $this->productOptionValueToNormalize->setCode('S'); + $this->productOptionValueToNormalize->setOption($sizeProductOption); + $this->productOptionValueToNormalize->setCurrentLocale('it_IT'); + $this->productOptionValueToNormalize->setFallbackLocale('en_US'); + + $sizeProductOptionValueTranslation = new ProductOptionValueTranslation(); + $sizeProductOptionValueTranslation->setLocale('en_US'); + $sizeProductOptionValueTranslation->setValue('Small'); + $this->productOptionValueToNormalize->addTranslation($sizeProductOptionValueTranslation); + + $sizeProductOptionValueTranslation = new ProductOptionValueTranslation(); + $sizeProductOptionValueTranslation->setLocale('it_IT'); + $sizeProductOptionValueTranslation->setValue('Piccola'); + $this->productOptionValueToNormalize->addTranslation($sizeProductOptionValueTranslation); + + $this->channel = new Channel(); + } + + public function testItIsInstantiable(): void + { + $this->assertInstanceOf(ProductOptionValueNormalizer::class, $this->productOptionValueNormalizer); + } + + public function testItIsAnInstanceOfNormalizer(): void + { + $this->assertInstanceOf(NormalizerInterface::class, $this->productOptionValueNormalizer); + } + + public function testItSupportProductVariantInterfaceType(): void + { + $supportedTypes= $this->productOptionValueNormalizer->getSupportedTypes(null); + + $this->assertArrayHasKey(ProductOptionValueInterface::class, $supportedTypes); + $this->assertTrue($supportedTypes[ProductOptionValueInterface::class]); + } + + public function testItSupportNormalizationWithRightType(): void + { + $this->assertTrue($this->productOptionValueNormalizer->supportsNormalization($this->productOptionValueToNormalize, null, ['type' => 'webgriffe_sylius_elasticsearch_plugin'])); + } + + public function testItDoesNotSupportNormalizationWithRightType(): void + { + $this->assertFalse($this->productOptionValueNormalizer->supportsNormalization($this->productOptionValueToNormalize, null, ['type' => 'other'])); + } + + public function testItDoesNotSupportNormalizationWithoutType(): void + { + $this->assertFalse($this->productOptionValueNormalizer->supportsNormalization($this->productOptionValueToNormalize)); + } + + public function testItNormalizeProductVariant(): void + { + $productOptionValueNormalized = $this->productOptionValueNormalizer->normalize($this->productOptionValueToNormalize, null, ['channel' => $this->channel]); + $this->assertIsArray($productOptionValueNormalized); + + $this->assertEquals(21, $productOptionValueNormalized['sylius-id']); + $this->assertEquals('S', $productOptionValueNormalized['code']); + $this->assertEquals('Piccola', $productOptionValueNormalized['value']); + $this->assertEquals([ + ['en_US' => 'Small'], + ['it_IT' => 'Piccola'], + ], $productOptionValueNormalized['name']); + } +} diff --git a/tests/Unit/Serializer/ProductVariantNormalizerTest.php b/tests/Unit/Serializer/ProductVariantNormalizerTest.php index 04d28da..9acd7c8 100644 --- a/tests/Unit/Serializer/ProductVariantNormalizerTest.php +++ b/tests/Unit/Serializer/ProductVariantNormalizerTest.php @@ -1,5 +1,7 @@ productVariantNormalizer = new ProductVariantNormalizer( - new EventDispatcher(), + $eventDispatcher, + new Serializer([new ProductOptionValueNormalizer($eventDispatcher)]), ); $this->productVariantToNormalize = new ProductVariant(); @@ -180,7 +186,7 @@ public function testItDoesNotSupportNormalizationWithoutType(): void public function testItNormalizeProductVariant(): void { - $productVariantNormalized = $this->productVariantNormalizer->normalize($this->productVariantToNormalize, null, ['channel' => $this->channel]); + $productVariantNormalized = $this->productVariantNormalizer->normalize($this->productVariantToNormalize, null, ['channel' => $this->channel, 'type' => 'webgriffe_sylius_elasticsearch_plugin']); $this->assertIsArray($productVariantNormalized); $this->assertEquals(1, $productVariantNormalized['sylius-id']);