vendor/ibexa/product-catalog/src/lib/Personalization/Event/Subscriber/ProductEventSubscriber.php line 148

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\Personalization\Event\Subscriber;
  8. use Ibexa\Contracts\Core\Repository\Iterator\BatchIterator;
  9. use Ibexa\Contracts\ProductCatalog\Events\CreatePriceEvent;
  10. use Ibexa\Contracts\ProductCatalog\Events\DeletePriceEvent;
  11. use Ibexa\Contracts\ProductCatalog\Events\ExecutePriceStructsEvent;
  12. use Ibexa\Contracts\ProductCatalog\Events\UpdatePriceEvent;
  13. use Ibexa\Contracts\ProductCatalog\Iterator\BatchIteratorAdapter\VariantFetchAdapter;
  14. use Ibexa\Contracts\ProductCatalog\Local\Events\BeforeDeleteBaseProductVariantsEvent;
  15. use Ibexa\Contracts\ProductCatalog\Local\Events\CreateProductVariantsEvent;
  16. use Ibexa\Contracts\ProductCatalog\Local\Events\DeleteProductEvent;
  17. use Ibexa\Contracts\ProductCatalog\Local\Events\UpdateProductEvent;
  18. use Ibexa\Contracts\ProductCatalog\Local\Events\UpdateProductVariantEvent;
  19. use Ibexa\Contracts\ProductCatalog\Permission\Policy\Product\Delete;
  20. use Ibexa\Contracts\ProductCatalog\PermissionResolverInterface;
  21. use Ibexa\Contracts\ProductCatalog\ProductServiceInterface as APIProductServiceInterface;
  22. use Ibexa\Contracts\ProductCatalog\Values\ContentAwareProductInterface;
  23. use Ibexa\Contracts\ProductCatalog\Values\ProductInterface;
  24. use Ibexa\Contracts\ProductCatalog\Values\ProductVariantInterface;
  25. use Ibexa\Personalization\Exception\InvalidArgumentException;
  26. use Ibexa\Personalization\Service\Content\ContentServiceInterface;
  27. use Ibexa\ProductCatalog\Personalization\Service\Product\ProductServiceInterface;
  28. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  29. final class ProductEventSubscriber implements EventSubscriberInterface
  30. {
  31. private const PRIORITY = -10;
  32. private ContentServiceInterface $contentService;
  33. private ProductServiceInterface $productService;
  34. private APIProductServiceInterface $apiProductService;
  35. private PermissionResolverInterface $permissionResolver;
  36. public function __construct(
  37. ContentServiceInterface $contentService,
  38. ProductServiceInterface $productService,
  39. APIProductServiceInterface $apiProductService,
  40. PermissionResolverInterface $permissionResolver
  41. ) {
  42. $this->contentService = $contentService;
  43. $this->productService = $productService;
  44. $this->apiProductService = $apiProductService;
  45. $this->permissionResolver = $permissionResolver;
  46. }
  47. public static function getSubscribedEvents(): array
  48. {
  49. return [
  50. CreatePriceEvent::class => ['onCreatePrice', self::PRIORITY],
  51. UpdatePriceEvent::class => ['onUpdatePrice', self::PRIORITY],
  52. DeletePriceEvent::class => ['onDeletePrice', self::PRIORITY],
  53. UpdateProductEvent::class => ['onUpdateProduct', self::PRIORITY],
  54. DeleteProductEvent::class => ['onDeleteProduct', self::PRIORITY],
  55. BeforeDeleteBaseProductVariantsEvent::class => ['onBeforeDeleteBaseProductVariants', self::PRIORITY],
  56. CreateProductVariantsEvent::class => ['onCreateProductVariants', self::PRIORITY],
  57. UpdateProductVariantEvent::class => ['onUpdateProductVariant', self::PRIORITY],
  58. ExecutePriceStructsEvent::class => ['onExecutePriceStructs', self::PRIORITY],
  59. ];
  60. }
  61. public function onCreatePrice(CreatePriceEvent $event): void
  62. {
  63. $this->sendNotificationForProduct($event->getCreateStruct()->getProduct());
  64. }
  65. public function onDeletePrice(DeletePriceEvent $event): void
  66. {
  67. $this->sendNotificationForProduct($event->getDeleteStruct()->getProduct());
  68. }
  69. public function onUpdateProduct(UpdateProductEvent $event): void
  70. {
  71. $product = $event->getProduct();
  72. if (!$product->isBaseProduct()) {
  73. return;
  74. }
  75. $variantIterator = new BatchIterator(
  76. new VariantFetchAdapter($this->apiProductService, $product),
  77. 25
  78. );
  79. $this->productService->updateVariants(iterator_to_array($variantIterator));
  80. }
  81. public function onDeleteProduct(DeleteProductEvent $event): void
  82. {
  83. $product = $event->getProduct();
  84. if ($product->isVariant()) {
  85. /** @var \Ibexa\Contracts\ProductCatalog\Values\ProductVariantInterface $product */
  86. $this->sendNotificationForDeleteVariant($product);
  87. }
  88. }
  89. public function onBeforeDeleteBaseProductVariants(BeforeDeleteBaseProductVariantsEvent $event): void
  90. {
  91. $baseProduct = $event->getBaseProduct();
  92. if (!$this->permissionResolver->canUser(new Delete($baseProduct)) || !$baseProduct->isBaseProduct()) {
  93. return;
  94. }
  95. $variantIterator = new BatchIterator(
  96. new VariantFetchAdapter($this->apiProductService, $baseProduct),
  97. 25
  98. );
  99. $this->productService->deleteVariants(iterator_to_array($variantIterator));
  100. }
  101. public function onCreateProductVariants(CreateProductVariantsEvent $event): void
  102. {
  103. $createStructs = $event->getCreateStructs();
  104. $variants = [];
  105. foreach ($createStructs as $createStruct) {
  106. $code = $createStruct->getCode();
  107. if ($code === null) {
  108. throw new InvalidArgumentException('ProductVariant code must be provided');
  109. }
  110. $variants[] = $this->apiProductService->getProductVariant($code);
  111. }
  112. $this->productService->updateVariants($variants);
  113. }
  114. public function onUpdateProductVariant(UpdateProductVariantEvent $event): void
  115. {
  116. $productVariant = $event->getProductVariant();
  117. $this->sendNotificationForUpdateVariant($productVariant);
  118. }
  119. public function onUpdatePrice(UpdatePriceEvent $event): void
  120. {
  121. $this->sendNotificationForProduct($event->getUpdateStruct()->getProduct());
  122. }
  123. public function onExecutePriceStructs(ExecutePriceStructsEvent $event): void
  124. {
  125. $productCodes = [];
  126. $products = [];
  127. foreach ($event->getPriceStructs() as $struct) {
  128. $product = $struct->getProduct();
  129. $productCode = $product->getCode();
  130. if (
  131. $product instanceof ContentAwareProductInterface
  132. && !in_array($productCode, $productCodes, true)
  133. ) {
  134. $productCodes[] = $productCode;
  135. $products[] = $product->getContent();
  136. }
  137. }
  138. $this->contentService->updateContentItems($products);
  139. }
  140. private function sendNotificationForProduct(ProductInterface $product): void
  141. {
  142. if (!$product instanceof ContentAwareProductInterface) {
  143. return;
  144. }
  145. $this->contentService->updateContent($product->getContent());
  146. }
  147. private function sendNotificationForDeleteVariant(ProductVariantInterface $productVariant): void
  148. {
  149. if (!$productVariant instanceof ContentAwareProductInterface) {
  150. return;
  151. }
  152. $this->productService->deleteVariant($productVariant);
  153. }
  154. private function sendNotificationForUpdateVariant(
  155. ProductVariantInterface $productVariant
  156. ): void {
  157. if (!$productVariant instanceof ContentAwareProductInterface) {
  158. return;
  159. }
  160. $this->productService->updateVariant($productVariant);
  161. }
  162. }