vendor/ibexa/product-catalog/src/lib/Event/NameSchemaSubscriber.php line 72

Open in your IDE?
  1. <?php
  2. /**
  3.  * @copyright Copyright (C) Ibexa AS. All rights reserved.
  4.  * @license For full copyright and license information view LICENSE file distributed with this source code.
  5.  */
  6. declare(strict_types=1);
  7. namespace Ibexa\ProductCatalog\Event;
  8. use Exception;
  9. use Ibexa\Contracts\Core\Event\NameSchema\AbstractNameSchemaEvent;
  10. use Ibexa\Contracts\Core\Event\NameSchema\AbstractSchemaEvent;
  11. use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent;
  12. use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent;
  13. use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent;
  14. use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
  15. use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException;
  16. use Ibexa\Contracts\Core\Repository\Values\Content\Content;
  17. use Ibexa\Contracts\Core\Repository\Values\Content\Language;
  18. use Ibexa\Contracts\ProductCatalog\AttributeDefinitionServiceInterface;
  19. use Ibexa\Contracts\ProductCatalog\NameSchema\NameSchemaStrategyInterface;
  20. use Ibexa\ProductCatalog\FieldType\ProductSpecification\Type;
  21. use Ibexa\ProductCatalog\FieldType\ProductSpecification\Value;
  22. use Psr\Log\LoggerInterface;
  23. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  24. /**
  25.  * @internal
  26.  */
  27. final class NameSchemaSubscriber implements EventSubscriberInterface
  28. {
  29.     public const ATTRIBUTE_KEY 'attribute';
  30.     public const SEPARATOR ':';
  31.     private AttributeDefinitionServiceInterface $definitionService;
  32.     private NameSchemaStrategyInterface $nameSchemaStrategy;
  33.     private LoggerInterface $logger;
  34.     public function __construct(
  35.         AttributeDefinitionServiceInterface $definitionService,
  36.         NameSchemaStrategyInterface $nameSchemaStrategy,
  37.         LoggerInterface $logger
  38.     ) {
  39.         $this->definitionService $definitionService;
  40.         $this->nameSchemaStrategy $nameSchemaStrategy;
  41.         $this->logger $logger;
  42.     }
  43.     public static function getSubscribedEvents(): array
  44.     {
  45.         return [
  46.             ResolveNameSchemaEvent::class => [
  47.                 'onResolveNameSchema',
  48.             ],
  49.             ResolveContentNameSchemaEvent::class => [
  50.                 'onResolveContentNameSchema',
  51.             ],
  52.             ResolveUrlAliasSchemaEvent::class => [
  53.                 'onResolveUrlAliasSchema',
  54.             ],
  55.         ];
  56.     }
  57.     public function onResolveNameSchema(ResolveNameSchemaEvent $event): void
  58.     {
  59.         $this->processSchema($event);
  60.     }
  61.     public function onResolveContentNameSchema(ResolveContentNameSchemaEvent $event): void
  62.     {
  63.         $this->processSchema($event);
  64.     }
  65.     public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void
  66.     {
  67.         if (!$this->isEventValid($event)) {
  68.             return;
  69.         }
  70.         $content $event->getContent();
  71.         $productSpecificationField $this->getProductSpecificationField($content);
  72.         if ($productSpecificationField === null) {
  73.             return;
  74.         }
  75.         $event->setTokenValues(
  76.             $this->processTokens(
  77.                 $event->getSchemaIdentifiers()[self::ATTRIBUTE_KEY],
  78.                 array_map(
  79.                     static function (Language $language): string {
  80.                         return $language->getLanguageCode();
  81.                     },
  82.                     (array) $content->getVersionInfo()->getLanguages()
  83.                 ),
  84.                 $productSpecificationField->getAttributes(),
  85.                 $event->getTokenValues()
  86.             )
  87.         );
  88.     }
  89.     private function processSchema(AbstractNameSchemaEvent $event): void
  90.     {
  91.         if (!$this->isEventValid($event)) {
  92.             return;
  93.         }
  94.         $contentType $event->getContentType();
  95.         /** @var \Ibexa\Core\Repository\Values\ContentType\FieldDefinition $specificationDefinition */
  96.         $specificationDefinition $contentType->getFirstFieldDefinitionOfType(Type::FIELD_TYPE_IDENTIFIER);
  97.         $productSpecificationField $this->getProductSpecificationFieldFromEvent(
  98.             $event,
  99.             $specificationDefinition->identifier
  100.         );
  101.         if ($productSpecificationField === null) {
  102.             return;
  103.         }
  104.         $tokenValues $this->processTokens(
  105.             $event->getSchemaIdentifiers()[self::ATTRIBUTE_KEY],
  106.             $event->getLanguageCodes(),
  107.             $productSpecificationField->getAttributes(),
  108.             $event->getTokenValues()
  109.         );
  110.         $tokenValues array_merge($event->getTokenValues(), $tokenValues);
  111.         $event->setTokenValues($tokenValues);
  112.     }
  113.     private function getProductSpecificationFieldFromEvent(AbstractNameSchemaEvent $eventstring $identifier): ?Value
  114.     {
  115.         /** @var array<\Ibexa\ProductCatalog\FieldType\ProductSpecification\Value> $productSpecificationField */
  116.         $productSpecificationField $event->getFieldMap()[$identifier] ?? [];
  117.         return reset($productSpecificationField) ?: null;
  118.     }
  119.     /**
  120.      * @param array<int, mixed> $attributes
  121.      * @param array<string, mixed> $tokenValues
  122.      */
  123.     private function processToken(string $tokenstring $languageCode, array $attributes, array &$tokenValues): void
  124.     {
  125.         try {
  126.             $attributeDefinition $this->definitionService->getAttributeDefinition($token);
  127.         } catch (NotFoundException|UnauthorizedException $e) {
  128.             $this->handleAttributeDefinitionException($e$token$languageCode$tokenValues);
  129.             return;
  130.         }
  131.         $attributeId $attributeDefinition->getId();
  132.         if (isset($attributes[$attributeId])) {
  133.             $key self::ATTRIBUTE_KEY self::SEPARATOR $token;
  134.             $value $this->nameSchemaStrategy->resolve(
  135.                 $attributeDefinition,
  136.                 $attributes[$attributeId],
  137.                 $languageCode
  138.             );
  139.             $tokenValues[$languageCode][$key] = $value;
  140.         } else {
  141.             $this->logger->warning(
  142.                 'Attribute value does not exist for given attribute definition id',
  143.                 [
  144.                     'attributes' => $attributes,
  145.                     'attribute_definition_id' => $attributeDefinition->getIdentifier(),
  146.                 ]
  147.             );
  148.         }
  149.     }
  150.     /**
  151.      * @param array<string, mixed> $tokenValues
  152.      */
  153.     private function handleAttributeDefinitionException(
  154.         Exception $exception,
  155.         string $token,
  156.         string $languageCode,
  157.         array &$tokenValues
  158.     ): void {
  159.         $this->logger->warning('Attribute definition does not exist for the given identifier', [
  160.             'exception' => $exception,
  161.         ]);
  162.         $key self::ATTRIBUTE_KEY $token;
  163.         $tokenValues[$languageCode][$key] = $token;
  164.     }
  165.     private function getProductSpecificationField(Content $content): ?Value
  166.     {
  167.         $specificationDefinition $content->getContentType()
  168.             ->getFirstFieldDefinitionOfType(Type::FIELD_TYPE_IDENTIFIER);
  169.         if ($specificationDefinition === null) {
  170.             return null;
  171.         }
  172.         /** @var \Ibexa\ProductCatalog\FieldType\ProductSpecification\Value $field */
  173.         $field $content->getFieldValue($specificationDefinition->identifier);
  174.         return $field;
  175.     }
  176.     private function isEventValid(AbstractSchemaEvent $event): bool
  177.     {
  178.         return array_key_exists(self::ATTRIBUTE_KEY$event->getSchemaIdentifiers());
  179.     }
  180.     /**
  181.      * @param array<string> $tokens
  182.      * @param array<string> $languageCodes
  183.      * @param array<int, mixed> $attributes
  184.      * @param array<string, array<string>> $tokenValues
  185.      *
  186.      * @return array<string, array<string>>
  187.      */
  188.     public function processTokens(
  189.         array $tokens,
  190.         array $languageCodes,
  191.         array $attributes,
  192.         array $tokenValues
  193.     ): array {
  194.         foreach ($languageCodes as $languageCode) {
  195.             foreach ($tokens as $token) {
  196.                 $this->processToken($token$languageCode$attributes$tokenValues);
  197.             }
  198.         }
  199.         return $tokenValues;
  200.     }
  201. }