Skip to content

Commit cce336a

Browse files
committed
ACP2E-368: Issue when adding multiple items in addProductsToCart graphQL mutation
1 parent 490236c commit cce336a

File tree

1 file changed

+107
-59
lines changed

1 file changed

+107
-59
lines changed

app/code/Magento/Quote/Model/Cart/AddProductsToCart.php

Lines changed: 107 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,6 @@ class AddProductsToCart
5151
*/
5252
private $productRepository;
5353

54-
/**
55-
* @var array
56-
*/
57-
private $errors = [];
58-
5954
/**
6055
* @var CartRepositoryInterface
6156
*/
@@ -71,22 +66,30 @@ class AddProductsToCart
7166
*/
7267
private $requestBuilder;
7368

69+
/**
70+
* @var \Magento\Quote\Model\QuoteFactory
71+
*/
72+
private $quoteFactory;
73+
7474
/**
7575
* @param ProductRepositoryInterface $productRepository
7676
* @param CartRepositoryInterface $cartRepository
7777
* @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
7878
* @param BuyRequestBuilder $requestBuilder
79+
* @param \Magento\Quote\Model\QuoteFactory $quoteFactory
7980
*/
8081
public function __construct(
8182
ProductRepositoryInterface $productRepository,
8283
CartRepositoryInterface $cartRepository,
8384
MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId,
84-
BuyRequestBuilder $requestBuilder
85+
BuyRequestBuilder $requestBuilder,
86+
\Magento\Quote\Model\QuoteFactory $quoteFactory
8587
) {
8688
$this->productRepository = $productRepository;
8789
$this->cartRepository = $cartRepository;
8890
$this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
8991
$this->requestBuilder = $requestBuilder;
92+
$this->quoteFactory = $quoteFactory;
9093
}
9194

9295
/**
@@ -101,33 +104,39 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa
101104
{
102105
$cartId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId);
103106
$cart = $this->cartRepository->get($cartId);
104-
105-
foreach ($cartItems as $cartItemPosition => $cartItem) {
106-
$this->addItemToCart($cart, $cartItem, $cartItemPosition);
107-
// reset cart items and addresses to clean cache
108-
$cart->setTotalsCollectedFlag(false);
109-
$cart->getItemsCollection()->clear();
110-
$cart->getAddressesCollection()->clear();
111-
foreach ($cart->getAddressesCollection() as $item) {
112-
$item->setQuote($cart);
113-
}
114-
}
115-
107+
$allErrors = [];
116108
if ($cart->getData('has_error')) {
117109
$errors = $cart->getErrors();
118110

119111
/** @var MessageInterface $error */
120112
foreach ($errors as $error) {
121-
$this->addError($error->getText());
113+
$allErrors[] = $this->createError($error->getText());
122114
}
123115
}
124116

125-
if (count($this->errors) !== 0) {
117+
foreach ($cartItems as $cartItemPosition => $cartItem) {
118+
$tempCart = $this->cloneQuote($cart);
119+
$tempCart->setHasError(false);
120+
$errors = $this->addItemToCart($tempCart, $cartItem, $cartItemPosition);
121+
if ($errors) {
122+
array_push($allErrors, ...$errors);
123+
} else {
124+
$cart->removeAllItems();
125+
foreach ($tempCart->getAllItems() as $quoteItem) {
126+
$quoteItem->setQuote($cart);
127+
$cart->addItem($quoteItem);
128+
}
129+
}
130+
}
131+
132+
$this->cartRepository->save($cart);
133+
134+
if (count($allErrors) !== 0) {
126135
/* Revert changes introduced by add to cart processes in case of an error */
127136
$cart->getItemsCollection()->clear();
128137
}
129138

130-
return $this->prepareErrorOutput($cart);
139+
return $this->prepareErrorOutput($cart, $allErrors);
131140
}
132141

133142
/**
@@ -136,59 +145,59 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa
136145
* @param CartInterface|Quote $cart
137146
* @param Data\CartItem $cartItem
138147
* @param int $cartItemPosition
148+
* @return array
139149
*/
140-
private function addItemToCart(CartInterface $cart, Data\CartItem $cartItem, int $cartItemPosition): void
150+
private function addItemToCart(CartInterface $cart, Data\CartItem $cartItem, int $cartItemPosition): array
141151
{
142152
$sku = $cartItem->getSku();
153+
$errors = [];
143154

144155
if ($cartItem->getQuantity() <= 0) {
145-
$this->addError(__('The product quantity should be greater than 0')->render());
146-
147-
return;
148-
}
149-
150-
try {
151-
$product = $this->productRepository->get($sku, false, null, true);
152-
} catch (NoSuchEntityException $e) {
153-
$this->addError(
154-
__('Could not find a product with SKU "%sku"', ['sku' => $sku])->render(),
155-
$cartItemPosition
156-
);
157-
158-
return;
159-
}
156+
$errors[] = $this->createError(__('The product quantity should be greater than 0')->render());
157+
} else {
158+
$product = null;
159+
try {
160+
$product = $this->productRepository->get($sku, false, null, true);
161+
} catch (NoSuchEntityException $e) {
162+
$errors[] = $this->createError(
163+
__('Could not find a product with SKU "%sku"', ['sku' => $sku])->render(),
164+
$cartItemPosition
165+
);
166+
}
160167

161-
try {
162-
$result = $cart->addProduct($product, $this->requestBuilder->build($cartItem));
163-
$this->cartRepository->save($cart);
164-
} catch (\Throwable $e) {
165-
$this->addError(
166-
__($e->getMessage())->render(),
167-
$cartItemPosition
168-
);
169-
$cart->setHasError(false);
170-
171-
return;
172-
}
168+
if ($product !== null) {
169+
$result = null;
170+
try {
171+
$result = $cart->addProduct($product, $this->requestBuilder->build($cartItem));
172+
} catch (\Throwable $e) {
173+
$errors[] = $this->createError(
174+
__($e->getMessage())->render(),
175+
$cartItemPosition
176+
);
177+
}
173178

174-
if (is_string($result)) {
175-
$errors = array_unique(explode("\n", $result));
176-
foreach ($errors as $error) {
177-
$this->addError(__($error)->render(), $cartItemPosition);
179+
if (is_string($result)) {
180+
$errors = array_unique(explode("\n", $result));
181+
foreach ($errors as $error) {
182+
$errors[] = $this->createError(__($error)->render(), $cartItemPosition);
183+
}
184+
}
178185
}
179186
}
187+
188+
return $errors;
180189
}
181190

182191
/**
183192
* Add order line item error
184193
*
185194
* @param string $message
186195
* @param int $cartItemPosition
187-
* @return void
196+
* @return Data\Error
188197
*/
189-
private function addError(string $message, int $cartItemPosition = 0): void
198+
private function createError(string $message, int $cartItemPosition = 0): Data\Error
190199
{
191-
$this->errors[] = new Data\Error(
200+
return new Data\Error(
192201
$message,
193202
$this->getErrorCode($message),
194203
$cartItemPosition
@@ -219,14 +228,53 @@ private function getErrorCode(string $message): string
219228
* Creates a new output from existing errors
220229
*
221230
* @param CartInterface $cart
231+
* @param array $errors
222232
* @return AddProductsToCartOutput
223233
*/
224-
private function prepareErrorOutput(CartInterface $cart): AddProductsToCartOutput
234+
private function prepareErrorOutput(CartInterface $cart, array $errors = []): AddProductsToCartOutput
225235
{
226-
$output = new AddProductsToCartOutput($cart, $this->errors);
227-
$this->errors = [];
236+
$output = new AddProductsToCartOutput($cart, $errors);
228237
$cart->setHasError(false);
229238

230239
return $output;
231240
}
241+
242+
/**
243+
* Create temporary quote, which will incapsulate non-checked data.
244+
*
245+
* Under unchecked data, means, some data that can not pass validation or etc
246+
*
247+
* @param Quote $quote
248+
* @return Quote
249+
*/
250+
private function cloneQuote(Quote $quote)
251+
{
252+
// copy data to temporary quote
253+
/** @var $temporaryQuote \Magento\Quote\Model\Quote */
254+
$temporaryQuote = $this->quoteFactory->create();
255+
$temporaryQuote->setData($quote->getData());
256+
$temporaryQuote->setId(null);//as it is clone, we need to flush ids
257+
$temporaryQuote->setStore($quote->getStore())->setIsSuperMode($quote->getIsSuperMode());
258+
/** @var Quote\Item $quoteItem */
259+
foreach ($quote->getAllItems() as $quoteItem) {
260+
$temporaryItem = clone $quoteItem;
261+
$temporaryItem->setQuote($temporaryQuote);
262+
$temporaryQuote->addItem($temporaryItem);
263+
$quoteItem->setClonnedItem($temporaryItem);
264+
265+
//Check for parent item
266+
$parentItem = null;
267+
if ($quoteItem->getParentItem()) {
268+
$parentItem = $quoteItem->getParentItem();
269+
$temporaryItem->setParentProductId(null);
270+
} elseif ($quoteItem->getParentProductId()) {
271+
$parentItem = $quote->getItemById($quoteItem->getParentProductId());
272+
}
273+
if ($parentItem && $parentItem->getClonnedItem()) {
274+
$temporaryItem->setParentItem($parentItem->getClonnedItem());
275+
}
276+
}
277+
278+
return $temporaryQuote;
279+
}
232280
}

0 commit comments

Comments
 (0)