vendor/shopware/core/Checkout/Cart/CartPersister.php line 52

  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(
  24.         private readonly Connection $connection,
  25.         private readonly EventDispatcherInterface $eventDispatcher,
  26.         private readonly CartSerializationCleaner $cartSerializationCleaner,
  27.         private readonly bool $compress
  28.     ) {
  29.     }
  30.     public function getDecorated(): AbstractCartPersister
  31.     {
  32.         throw new DecorationPatternException(self::class);
  33.     }
  34.     public function load(string $tokenSalesChannelContext $context): Cart
  35.     {
  36.         // @deprecated tag:v6.6.0 - remove else part
  37.         if ($this->payloadExists()) {
  38.             $content $this->connection->fetchAssociative(
  39.                 '#cart-persister::load
  40.                 SELECT `cart`.`payload`, `cart`.`rule_ids`, `cart`.`compressed` FROM cart WHERE `token` = :token',
  41.                 ['token' => $token]
  42.             );
  43.         } else {
  44.             $content $this->connection->fetchAssociative(
  45.                 '#cart-persister::load
  46.                 SELECT `cart`.`cart` as payload, `cart`.`rule_ids`, 0 as `compressed` FROM cart WHERE `token` = :token',
  47.                 ['token' => $token]
  48.             );
  49.         }
  50.         if (!\is_array($content)) {
  51.             throw CartException::tokenNotFound($token);
  52.         }
  53.         $cart $content['compressed'] ? CacheValueCompressor::uncompress($content['payload']) : unserialize((string) $content['payload']);
  54.         if (!$cart instanceof Cart) {
  55.             throw CartException::deserializeFailed();
  56.         }
  57.         $cart->setToken($token);
  58.         $cart->setRuleIds(json_decode((string) $content['rule_ids'], true512\JSON_THROW_ON_ERROR) ?? []);
  59.         return $cart;
  60.     }
  61.     /**
  62.      * @throws InvalidUuidException
  63.      */
  64.     public function save(Cart $cartSalesChannelContext $context): void
  65.     {
  66.         if ($cart->getBehavior()?->isRecalculation()) {
  67.             return;
  68.         }
  69.         $shouldPersist $this->shouldPersist($cart);
  70.         $event = new CartVerifyPersistEvent($context$cart$shouldPersist);
  71.         $this->eventDispatcher->dispatch($event);
  72.         if (!$event->shouldBePersisted()) {
  73.             $this->delete($cart->getToken(), $context);
  74.             return;
  75.         }
  76.         $payloadExists $this->payloadExists();
  77.         $sql = <<<'SQL'
  78.             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`)
  79.             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)
  80.             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;
  81.         SQL;
  82.         if ($payloadExists) {
  83.             $sql = <<<'SQL'
  84.                 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`)
  85.                 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)
  86.                 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;
  87.             SQL;
  88.         }
  89.         $customerId $context->getCustomer() ? Uuid::fromHexToBytes($context->getCustomer()->getId()) : null;
  90.         $data = [
  91.             'token' => $cart->getToken(),
  92.             'currency_id' => Uuid::fromHexToBytes($context->getCurrency()->getId()),
  93.             'shipping_method_id' => Uuid::fromHexToBytes($context->getShippingMethod()->getId()),
  94.             'payment_method_id' => Uuid::fromHexToBytes($context->getPaymentMethod()->getId()),
  95.             'country_id' => Uuid::fromHexToBytes($context->getShippingLocation()->getCountry()->getId()),
  96.             'sales_channel_id' => Uuid::fromHexToBytes($context->getSalesChannel()->getId()),
  97.             'customer_id' => $customerId,
  98.             'price' => $cart->getPrice()->getTotalPrice(),
  99.             'line_item_count' => $cart->getLineItems()->count(),
  100.             'payload' => $this->serializeCart($cart$payloadExists),
  101.             'rule_ids' => json_encode($context->getRuleIds(), \JSON_THROW_ON_ERROR),
  102.             'now' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
  103.         ];
  104.         // @deprecated tag:v6.6.0 - remove if condition, but keep body
  105.         if ($payloadExists) {
  106.             $data['compressed'] = (int) $this->compress;
  107.         }
  108.         $query = new RetryableQuery($this->connection$this->connection->prepare($sql));
  109.         $query->execute($data);
  110.         $this->eventDispatcher->dispatch(new CartSavedEvent($context$cart));
  111.     }
  112.     public function delete(string $tokenSalesChannelContext $context): void
  113.     {
  114.         $query = new RetryableQuery(
  115.             $this->connection,
  116.             $this->connection->prepare('DELETE FROM `cart` WHERE `token` = :token')
  117.         );
  118.         $query->execute(['token' => $token]);
  119.     }
  120.     public function replace(string $oldTokenstring $newTokenSalesChannelContext $context): void
  121.     {
  122.         $this->connection->executeStatement(
  123.             'UPDATE `cart` SET `token` = :newToken WHERE `token` = :oldToken',
  124.             ['newToken' => $newToken'oldToken' => $oldToken]
  125.         );
  126.     }
  127.     /**
  128.      * @deprecated tag:v6.6.0 - will be removed
  129.      */
  130.     private function payloadExists(): bool
  131.     {
  132.         return EntityDefinitionQueryHelper::columnExists($this->connection'cart''payload');
  133.     }
  134.     private function serializeCart(Cart $cartbool $payloadExists): string
  135.     {
  136.         $errors $cart->getErrors();
  137.         $data $cart->getData();
  138.         $cart->setErrors(new ErrorCollection());
  139.         $cart->setData(null);
  140.         $this->cartSerializationCleaner->cleanupCart($cart);
  141.         // @deprecated tag:v6.6.0 - remove else part
  142.         if ($payloadExists) {
  143.             $serialized $this->compress CacheValueCompressor::compress($cart) : serialize($cart);
  144.         } else {
  145.             $serialized serialize($cart);
  146.         }
  147.         $cart->setErrors($errors);
  148.         $cart->setData($data);
  149.         return $serialized;
  150.     }
  151. }