Skip to content

Commit

Permalink
Extract product option value normalizer
Browse files Browse the repository at this point in the history
  • Loading branch information
lruozzi9 committed Sep 30, 2024
1 parent ee897d5 commit fdf203c
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 29 deletions.
10 changes: 10 additions & 0 deletions config/services/serializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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'),
])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Webgriffe\SyliusElasticsearchPlugin\Event\ProductDocumentType;

use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Product\Model\ProductOptionValueInterface;
use Symfony\Contracts\EventDispatcher\Event;

final class ProductDocumentTypeProductOptionValueNormalizeEvent extends Event
{
public function __construct(
private readonly ProductOptionValueInterface $optionValue,
private readonly ChannelInterface $channel,
private array $normalizedProductOptionValue,
) {
}

public function getOptionValue(): ProductOptionValueInterface
{
return $this->optionValue;
}

public function getChannel(): ChannelInterface
{
return $this->channel;
}

public function getNormalizedProductOptionValue(): array
{
return $this->normalizedProductOptionValue;
}

public function setNormalizedProductOptionValue(array $normalizedProductOptionValue): void
{
$this->normalizedProductOptionValue = $normalizedProductOptionValue;
}
}
3 changes: 1 addition & 2 deletions src/Serializer/ProductNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
) {
}
Expand Down
65 changes: 65 additions & 0 deletions src/Serializer/ProductOptionValueNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Webgriffe\SyliusElasticsearchPlugin\Serializer;

use Psr\EventDispatcher\EventDispatcherInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Product\Model\ProductOptionValueInterface;
use Sylius\Component\Product\Model\ProductOptionValueTranslationInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Webgriffe\SyliusElasticsearchPlugin\Event\ProductDocumentType\ProductDocumentTypeProductOptionValueNormalizeEvent;
use Webmozart\Assert\Assert;

final class ProductOptionValueNormalizer implements NormalizerInterface
{
public function __construct(
private readonly EventDispatcherInterface $eventDispatcher,
) {
}

/**
* @param ProductOptionValueInterface|mixed $object
*/
public function normalize(mixed $object, ?string $format = null, array $context = []): array
{
$channel = $context['channel'];
$optionValue = $object;
Assert::isInstanceOf($optionValue, ProductOptionValueInterface::class);
Assert::isInstanceOf($channel, ChannelInterface::class);

$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(),
];
}

$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'
;
}
}
33 changes: 10 additions & 23 deletions src/Serializer/ProductVariantNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@
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;
use Webgriffe\SyliusElasticsearchPlugin\Event\ProductDocumentType\ProductDocumentTypeProductVariantNormalizeEvent;
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,
) {
}

Expand Down Expand Up @@ -76,6 +79,8 @@ public function normalize(mixed $object, ?string $format = null, array $context
$normalizedVariant['options'][] = $this->normalizeProductOptionAndProductOptionValue(
$optionAndValue['option'],
$optionAndValue['value'],
$format,
$context,
);
}

Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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;
}
}
8 changes: 6 additions & 2 deletions tests/Unit/Serializer/ProductNormalizerTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Tests\Webgriffe\SyliusElasticsearchPlugin\Unit\Serializer;

use PHPUnit\Framework\TestCase;
Expand All @@ -11,6 +13,7 @@
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Serializer;
use Webgriffe\SyliusElasticsearchPlugin\Serializer\ProductNormalizer;
use Webgriffe\SyliusElasticsearchPlugin\Serializer\ProductOptionValueNormalizer;
use Webgriffe\SyliusElasticsearchPlugin\Serializer\ProductVariantNormalizer;

class ProductNormalizerTest extends TestCase
Expand All @@ -23,10 +26,11 @@ class ProductNormalizerTest extends TestCase
protected function setUp(): void
{
$eventDispatcher = new EventDispatcher();
$serializer = new Serializer([new ProductVariantNormalizer($eventDispatcher, new Serializer([new ProductOptionValueNormalizer($eventDispatcher)]))]);
$this->productNormalizer = new ProductNormalizer(
new DefaultProductVariantResolver(),
$eventDispatcher,
new Serializer([new ProductVariantNormalizer($eventDispatcher)]),
$serializer,
'en_US',
);

Expand Down Expand Up @@ -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']);
Expand Down
101 changes: 101 additions & 0 deletions tests/Unit/Serializer/ProductOptionValueNormalizerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

declare(strict_types=1);

namespace Tests\Webgriffe\SyliusElasticsearchPlugin\Unit\Serializer;

use PHPUnit\Framework\TestCase;
use Sylius\Component\Core\Model\Channel;
use Sylius\Component\Product\Model\ProductOptionValue;
use Sylius\Component\Product\Model\ProductOptionValueInterface;
use Sylius\Component\Product\Model\ProductOptionValueTranslation;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Tests\Webgriffe\SyliusElasticsearchPlugin\App\Entity\Product\ProductOption;
use Webgriffe\SyliusElasticsearchPlugin\Serializer\ProductOptionValueNormalizer;

class ProductOptionValueNormalizerTest extends TestCase
{
private ProductOptionValueNormalizer $productOptionValueNormalizer;
private ProductOptionValueInterface $productOptionValueToNormalize;
private Channel $channel;

protected function setUp(): void
{
$this->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']);
}
}
Loading

0 comments on commit fdf203c

Please sign in to comment.