1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
|
3
|
|
|
namespace Shopware\Core\Checkout\Cart; |
4
|
|
|
|
5
|
|
|
use Doctrine\DBAL\Connection; |
6
|
|
|
use Shopware\Core\Checkout\Cart\Error\ErrorCollection; |
7
|
|
|
use Shopware\Core\Checkout\Cart\Event\CartSavedEvent; |
8
|
|
|
use Shopware\Core\Checkout\Cart\Event\CartVerifyPersistEvent; |
9
|
|
|
use Shopware\Core\Defaults; |
10
|
|
|
use Shopware\Core\Framework\Adapter\Cache\CacheValueCompressor; |
11
|
|
|
use Shopware\Core\Framework\DataAbstractionLayer\Dbal\EntityDefinitionQueryHelper; |
12
|
|
|
use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\RetryableQuery; |
13
|
|
|
use Shopware\Core\Framework\Log\Package; |
14
|
|
|
use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException; |
15
|
|
|
use Shopware\Core\Framework\Uuid\Exception\InvalidUuidException; |
16
|
|
|
use Shopware\Core\Framework\Uuid\Uuid; |
17
|
|
|
use Shopware\Core\System\SalesChannel\SalesChannelContext; |
18
|
|
|
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; |
19
|
|
|
|
20
|
|
|
#[Package('checkout')] |
21
|
|
|
class CartPersister extends AbstractCartPersister |
22
|
|
|
{ |
23
|
|
|
/** |
24
|
|
|
* @internal |
25
|
|
|
*/ |
26
|
|
|
public function __construct( |
27
|
|
|
private readonly Connection $connection, |
28
|
|
|
private readonly EventDispatcherInterface $eventDispatcher, |
29
|
|
|
private readonly CartSerializationCleaner $cartSerializationCleaner, |
30
|
|
|
private readonly bool $compress |
31
|
|
|
) { |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
public function getDecorated(): AbstractCartPersister |
35
|
|
|
{ |
36
|
|
|
throw new DecorationPatternException(self::class); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
public function load(string $token, SalesChannelContext $context): Cart |
40
|
|
|
{ |
41
|
|
|
// @deprecated tag:v6.6.0 - remove else part |
42
|
|
|
if ($this->payloadExists()) { |
|
|
|
|
43
|
|
|
$content = $this->connection->fetchAssociative( |
44
|
|
|
'#cart-persister::load |
45
|
|
|
SELECT `cart`.`payload`, `cart`.`rule_ids`, `cart`.`compressed` FROM cart WHERE `token` = :token', |
46
|
|
|
['token' => $token] |
47
|
|
|
); |
48
|
|
|
} else { |
49
|
|
|
$content = $this->connection->fetchAssociative( |
50
|
|
|
'#cart-persister::load |
51
|
|
|
SELECT `cart`.`cart` as payload, `cart`.`rule_ids`, 0 as `compressed` FROM cart WHERE `token` = :token', |
52
|
|
|
['token' => $token] |
53
|
|
|
); |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
if (!\is_array($content)) { |
57
|
|
|
throw CartException::tokenNotFound($token); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
$cart = $content['compressed'] ? CacheValueCompressor::uncompress($content['payload']) : unserialize((string) $content['payload']); |
61
|
|
|
|
62
|
|
|
if (!$cart instanceof Cart) { |
63
|
|
|
throw CartException::deserializeFailed(); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
$cart->setToken($token); |
67
|
|
|
$cart->setRuleIds(json_decode((string) $content['rule_ids'], true, 512, \JSON_THROW_ON_ERROR) ?? []); |
68
|
|
|
|
69
|
|
|
return $cart; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @throws InvalidUuidException |
74
|
|
|
*/ |
75
|
|
|
public function save(Cart $cart, SalesChannelContext $context): void |
76
|
|
|
{ |
77
|
|
|
if ($cart->getBehavior()?->isRecalculation()) { |
78
|
|
|
return; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
$shouldPersist = $this->shouldPersist($cart); |
82
|
|
|
|
83
|
|
|
$event = new CartVerifyPersistEvent($context, $cart, $shouldPersist); |
84
|
|
|
$this->eventDispatcher->dispatch($event); |
85
|
|
|
|
86
|
|
|
if (!$event->shouldBePersisted()) { |
87
|
|
|
$this->delete($cart->getToken(), $context); |
88
|
|
|
|
89
|
|
|
return; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
$payloadExists = $this->payloadExists(); |
|
|
|
|
93
|
|
|
|
94
|
|
|
$sql = <<<'SQL' |
95
|
|
|
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`) |
96
|
|
|
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) |
97
|
|
|
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; |
98
|
|
|
SQL; |
99
|
|
|
|
100
|
|
|
if ($payloadExists) { |
101
|
|
|
$sql = <<<'SQL' |
102
|
|
|
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`) |
103
|
|
|
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) |
104
|
|
|
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; |
105
|
|
|
SQL; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
$customerId = $context->getCustomer() ? Uuid::fromHexToBytes($context->getCustomer()->getId()) : null; |
109
|
|
|
|
110
|
|
|
$data = [ |
111
|
|
|
'token' => $cart->getToken(), |
112
|
|
|
'currency_id' => Uuid::fromHexToBytes($context->getCurrency()->getId()), |
113
|
|
|
'shipping_method_id' => Uuid::fromHexToBytes($context->getShippingMethod()->getId()), |
114
|
|
|
'payment_method_id' => Uuid::fromHexToBytes($context->getPaymentMethod()->getId()), |
115
|
|
|
'country_id' => Uuid::fromHexToBytes($context->getShippingLocation()->getCountry()->getId()), |
116
|
|
|
'sales_channel_id' => Uuid::fromHexToBytes($context->getSalesChannel()->getId()), |
117
|
|
|
'customer_id' => $customerId, |
118
|
|
|
'price' => $cart->getPrice()->getTotalPrice(), |
119
|
|
|
'line_item_count' => $cart->getLineItems()->count(), |
120
|
|
|
'payload' => $this->serializeCart($cart, $payloadExists), |
121
|
|
|
'rule_ids' => json_encode($context->getRuleIds(), \JSON_THROW_ON_ERROR), |
122
|
|
|
'now' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT), |
123
|
|
|
]; |
124
|
|
|
|
125
|
|
|
// @deprecated tag:v6.6.0 - remove if condition, but keep body |
126
|
|
|
if ($payloadExists) { |
127
|
|
|
$data['compressed'] = (int) $this->compress; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
$query = new RetryableQuery($this->connection, $this->connection->prepare($sql)); |
131
|
|
|
$query->execute($data); |
132
|
|
|
|
133
|
|
|
$this->eventDispatcher->dispatch(new CartSavedEvent($context, $cart)); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
public function delete(string $token, SalesChannelContext $context): void |
137
|
|
|
{ |
138
|
|
|
$query = new RetryableQuery( |
139
|
|
|
$this->connection, |
140
|
|
|
$this->connection->prepare('DELETE FROM `cart` WHERE `token` = :token') |
141
|
|
|
); |
142
|
|
|
$query->execute(['token' => $token]); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function replace(string $oldToken, string $newToken, SalesChannelContext $context): void |
146
|
|
|
{ |
147
|
|
|
$this->connection->executeStatement( |
148
|
|
|
'UPDATE `cart` SET `token` = :newToken WHERE `token` = :oldToken', |
149
|
|
|
['newToken' => $newToken, 'oldToken' => $oldToken] |
150
|
|
|
); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* @deprecated tag:v6.6.0 - will be removed |
155
|
|
|
*/ |
156
|
|
|
private function payloadExists(): bool |
157
|
|
|
{ |
158
|
|
|
return EntityDefinitionQueryHelper::columnExists($this->connection, 'cart', 'payload'); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
private function serializeCart(Cart $cart, bool $payloadExists): string |
162
|
|
|
{ |
163
|
|
|
$errors = $cart->getErrors(); |
164
|
|
|
$data = $cart->getData(); |
165
|
|
|
|
166
|
|
|
$cart->setErrors(new ErrorCollection()); |
167
|
|
|
$cart->setData(null); |
168
|
|
|
|
169
|
|
|
$this->cartSerializationCleaner->cleanupCart($cart); |
170
|
|
|
|
171
|
|
|
// @deprecated tag:v6.6.0 - remove else part |
172
|
|
|
if ($payloadExists) { |
173
|
|
|
$serialized = $this->compress ? CacheValueCompressor::compress($cart) : serialize($cart); |
174
|
|
|
} else { |
175
|
|
|
$serialized = serialize($cart); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
$cart->setErrors($errors); |
179
|
|
|
$cart->setData($data); |
180
|
|
|
|
181
|
|
|
return $serialized; |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.