src/Core/Checkout/Cart/CartPersister.php line 38

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Checkout\Cart;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Checkout\Cart\Error\ErrorCollection;
  5. use Shopware\Core\Checkout\Cart\Event\CartSavedEvent;
  6. use Shopware\Core\Checkout\Cart\Event\CartVerifyPersistEvent;
  7. use Shopware\Core\Defaults;
  8. use Shopware\Core\Framework\Adapter\Cache\CacheValueCompressor;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Dbal\EntityDefinitionQueryHelper;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\RetryableQuery;
  11. use Shopware\Core\Framework\Log\Package;
  12. use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException;
  13. use Shopware\Core\Framework\Uuid\Exception\InvalidUuidException;
  14. use Shopware\Core\Framework\Uuid\Uuid;
  15. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  16. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  17. #[Package('checkout')]
  18. class CartPersister extends AbstractCartPersister
  19. {
  20.     /**
  21.      * @internal
  22.      */
  23.     public function __construct(private readonly Connection $connection, private readonly EventDispatcherInterface $eventDispatcher, private readonly CartSerializationCleaner $cartSerializationCleaner, private readonly bool $compress)
  24.     {
  25.     }
  26.     public function getDecorated(): AbstractCartPersister
  27.     {
  28.         throw new DecorationPatternException(self::class);
  29.     }
  30.     public function load(string $tokenSalesChannelContext $context): Cart
  31.     {
  32.         // @deprecated tag:v6.6.0 - remove else part
  33.         if ($this->payloadExists()) {
  34.             $content $this->connection->fetchAssociative(
  35.                 '#cart-persister::load
  36.                 SELECT `cart`.`payload`, `cart`.`rule_ids`, `cart`.`compressed` FROM cart WHERE `token` = :token',
  37.                 ['token' => $token]
  38.             );
  39.         } else {
  40.             $content $this->connection->fetchAssociative(
  41.                 '#cart-persister::load
  42.                 SELECT `cart`.`cart` as payload, `cart`.`rule_ids`, 0 as `compressed` FROM cart WHERE `token` = :token',
  43.                 ['token' => $token]
  44.             );
  45.         }
  46.         if (!\is_array($content)) {
  47.             throw CartException::tokenNotFound($token);
  48.         }
  49.         $cart $content['compressed'] ? CacheValueCompressor::uncompress($content['payload']) : unserialize((string) $content['payload']);
  50.         if (!$cart instanceof Cart) {
  51.             throw CartException::deserializeFailed();
  52.         }
  53.         $cart->setToken($token);
  54.         $cart->setRuleIds(json_decode((string) $content['rule_ids'], true512\JSON_THROW_ON_ERROR) ?? []);
  55.         return $cart;
  56.     }
  57.     /**
  58.      * @throws InvalidUuidException
  59.      */
  60.     public function save(Cart $cartSalesChannelContext $context): void
  61.     {
  62.         $shouldPersist $this->shouldPersist($cart);
  63.         $event = new CartVerifyPersistEvent($context$cart$shouldPersist);
  64.         $this->eventDispatcher->dispatch($event);
  65.         if (!$event->shouldBePersisted()) {
  66.             $this->delete($cart->getToken(), $context);
  67.             return;
  68.         }
  69.         $payloadExists $this->payloadExists();
  70.         $sql = <<<'SQL'
  71.             INSERT INTO `cart` (`token`, `currency_id`, `shipping_method_id`, `payment_method_id`, `country_id`, `sales_channel_id`, `customer_id`, `price`, `line_item_count`, `cart`, `rule_ids`, `created_at`)
  72.             VALUES (:token, :currency_id, :shipping_method_id, :payment_method_id, :country_id, :sales_channel_id, :customer_id, :price, :line_item_count, :payload, :rule_ids, :now)
  73.             ON DUPLICATE KEY UPDATE `currency_id` = :currency_id, `shipping_method_id` = :shipping_method_id, `payment_method_id` = :payment_method_id, `country_id` = :country_id, `sales_channel_id` = :sales_channel_id, `customer_id` = :customer_id,`price` = :price, `line_item_count` = :line_item_count, `cart` = :payload, `rule_ids` = :rule_ids, `updated_at` = :now;
  74.         SQL;
  75.         if ($payloadExists) {
  76.             $sql = <<<'SQL'
  77.                 INSERT INTO `cart` (`token`, `currency_id`, `shipping_method_id`, `payment_method_id`, `country_id`, `sales_channel_id`, `customer_id`, `price`, `line_item_count`, `payload`, `rule_ids`, `compressed`, `created_at`)
  78.                 VALUES (:token, :currency_id, :shipping_method_id, :payment_method_id, :country_id, :sales_channel_id, :customer_id, :price, :line_item_count, :payload, :rule_ids, :compressed, :now)
  79.                 ON DUPLICATE KEY UPDATE `currency_id` = :currency_id, `shipping_method_id` = :shipping_method_id, `payment_method_id` = :payment_method_id, `country_id` = :country_id, `sales_channel_id` = :sales_channel_id, `customer_id` = :customer_id,`price` = :price, `line_item_count` = :line_item_count, `payload` = :payload, `compressed` = :compressed, `rule_ids` = :rule_ids, `updated_at` = :now;
  80.             SQL;
  81.         }
  82.         $customerId $context->getCustomer() ? Uuid::fromHexToBytes($context->getCustomer()->getId()) : null;
  83.         $data = [
  84.             'token' => $cart->getToken(),
  85.             'currency_id' => Uuid::fromHexToBytes($context->getCurrency()->getId()),
  86.             'shipping_method_id' => Uuid::fromHexToBytes($context->getShippingMethod()->getId()),
  87.             'payment_method_id' => Uuid::fromHexToBytes($context->getPaymentMethod()->getId()),
  88.             'country_id' => Uuid::fromHexToBytes($context->getShippingLocation()->getCountry()->getId()),
  89.             'sales_channel_id' => Uuid::fromHexToBytes($context->getSalesChannel()->getId()),
  90.             'customer_id' => $customerId,
  91.             'price' => $cart->getPrice()->getTotalPrice(),
  92.             'line_item_count' => $cart->getLineItems()->count(),
  93.             'payload' => $this->serializeCart($cart$payloadExists),
  94.             'rule_ids' => json_encode($context->getRuleIds(), \JSON_THROW_ON_ERROR),
  95.             'now' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
  96.         ];
  97.         // @deprecated tag:v6.6.0 - remove if condition, but keep body
  98.         if ($payloadExists) {
  99.             $data['compressed'] = (int) $this->compress;
  100.         }
  101.         $query = new RetryableQuery($this->connection$this->connection->prepare($sql));
  102.         $query->execute($data);
  103.         $this->eventDispatcher->dispatch(new CartSavedEvent($context$cart));
  104.     }
  105.     public function delete(string $tokenSalesChannelContext $context): void
  106.     {
  107.         $query = new RetryableQuery(
  108.             $this->connection,
  109.             $this->connection->prepare('DELETE FROM `cart` WHERE `token` = :token')
  110.         );
  111.         $query->execute(['token' => $token]);
  112.     }
  113.     public function replace(string $oldTokenstring $newTokenSalesChannelContext $context): void
  114.     {
  115.         $this->connection->executeStatement(
  116.             'UPDATE `cart` SET `token` = :newToken WHERE `token` = :oldToken',
  117.             ['newToken' => $newToken'oldToken' => $oldToken]
  118.         );
  119.     }
  120.     /**
  121.      * @deprecated tag:v6.6.0 - will be removed
  122.      */
  123.     private function payloadExists(): bool
  124.     {
  125.         return EntityDefinitionQueryHelper::columnExists($this->connection'cart''payload');
  126.     }
  127.     private function serializeCart(Cart $cartbool $payloadExists): string
  128.     {
  129.         $errors $cart->getErrors();
  130.         $data $cart->getData();
  131.         $cart->setErrors(new ErrorCollection());
  132.         $cart->setData(null);
  133.         $this->cartSerializationCleaner->cleanupCart($cart);
  134.         // @deprecated tag:v6.6.0 - remove else part
  135.         if ($payloadExists) {
  136.             $serialized $this->compress CacheValueCompressor::compress($cart) : serialize($cart);
  137.         } else {
  138.             $serialized serialize($cart);
  139.         }
  140.         $cart->setErrors($errors);
  141.         $cart->setData($data);
  142.         return $serialized;
  143.     }
  144. }