vendor/shopware/core/Content/Product/Cart/ProductLineItemCommandValidator.php line 45

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Product\Cart;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Checkout\Cart\LineItem\LineItem;
  5. use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemDefinition;
  6. use Shopware\Core\Content\Product\Exception\ProductLineItemDifferentIdException;
  7. use Shopware\Core\Content\Product\Exception\ProductLineItemInconsistentException;
  8. use Shopware\Core\Defaults;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\SetNullOnDeleteCommand;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\UpdateCommand;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\WriteCommand;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;
  13. use Shopware\Core\Framework\Log\Package;
  14. use Shopware\Core\Framework\Uuid\Uuid;
  15. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  16. /**
  17.  * @deprecated tag:v6.5.0 - reason:becomes-internal - EventSubscribers will become internal in v6.5.0
  18.  */
  19. #[Package('inventory')]
  20. class ProductLineItemCommandValidator implements EventSubscriberInterface
  21. {
  22.     private Connection $connection;
  23.     /**
  24.      * @internal
  25.      */
  26.     public function __construct(Connection $connection)
  27.     {
  28.         $this->connection $connection;
  29.     }
  30.     /**
  31.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  32.      */
  33.     public static function getSubscribedEvents()
  34.     {
  35.         return [
  36.             PreWriteValidationEvent::class => 'preValidate',
  37.         ];
  38.     }
  39.     public function preValidate(PreWriteValidationEvent $event): void
  40.     {
  41.         if ($event->getContext()->getVersionId() !== Defaults::LIVE_VERSION) {
  42.             return;
  43.         }
  44.         $products $this->findProducts($event->getCommands());
  45.         foreach ($event->getCommands() as $command) {
  46.             if ($command->getDefinition()->getClass() !== OrderLineItemDefinition::class) {
  47.                 continue;
  48.             }
  49.             if ($command instanceof SetNullOnDeleteCommand) {
  50.                 continue;
  51.             }
  52.             $payload $command->getPayload();
  53.             $lineItemId Uuid::fromBytesToHex($command->getPrimaryKey()['id']);
  54.             $productIdChanged = \array_key_exists('product_id'$payload);
  55.             $referenceIdChanged = \array_key_exists('referenced_id'$payload);
  56.             $lineItemPayload = isset($payload['payload']) ? json_decode($payload['payload'], true) : [];
  57.             $orderNumberChanged = \array_key_exists('productNumber'$lineItemPayload);
  58.             if (!$this->isProduct($products$payload$lineItemId)) {
  59.                 continue;
  60.             }
  61.             $somethingChanged $productIdChanged || $referenceIdChanged || $orderNumberChanged;
  62.             $allChanged $productIdChanged && $referenceIdChanged && $orderNumberChanged;
  63.             // has a field changed?
  64.             if (!$somethingChanged) {
  65.                 continue;
  66.             }
  67.             $productId = isset($payload['product_id']) ? Uuid::fromBytesToHex($payload['product_id']) : null;
  68.             $referenceId $payload['referenced_id'] ?? null;
  69.             if ($productId !== $referenceId) {
  70.                 $event->getExceptions()->add(
  71.                     new ProductLineItemDifferentIdException($lineItemId)
  72.                 );
  73.             }
  74.             // all fields updated? everything is consistent
  75.             if ($allChanged) {
  76.                 continue;
  77.             }
  78.             $event->getExceptions()->add(
  79.                 new ProductLineItemInconsistentException($lineItemId)
  80.             );
  81.         }
  82.     }
  83.     /**
  84.      * @param list<WriteCommand> $commands
  85.      *
  86.      * @return array<string, int>
  87.      */
  88.     private function findProducts(array $commands): array
  89.     {
  90.         $ids array_map(function (WriteCommand $command) {
  91.             if ($command->getDefinition()->getClass() !== OrderLineItemDefinition::class) {
  92.                 return null;
  93.             }
  94.             if ($command instanceof UpdateCommand) {
  95.                 return $command->getPrimaryKey()['id'];
  96.             }
  97.             return null;
  98.         }, $commands);
  99.         $ids array_values(array_filter($ids));
  100.         if (empty($ids)) {
  101.             return [];
  102.         }
  103.         /** @var array<string, int> $products */
  104.         $products = \array_flip($this->connection->fetchFirstColumn(
  105.             'SELECT DISTINCT LOWER(HEX(id)) FROM order_line_item WHERE id IN (:ids) AND type = \'product\'',
  106.             ['ids' => $ids],
  107.             ['ids' => Connection::PARAM_STR_ARRAY]
  108.         ));
  109.         return $products;
  110.     }
  111.     /**
  112.      * @param array<string, mixed> $products
  113.      * @param array<string, mixed> $payload
  114.      */
  115.     private function isProduct(array $products, array $payloadstring $lineItemId): bool
  116.     {
  117.         if (isset($payload['type'])) {
  118.             return $payload['type'] === LineItem::PRODUCT_LINE_ITEM_TYPE;
  119.         }
  120.         return isset($products[$lineItemId]);
  121.     }
  122. }