@@ -51,11 +51,6 @@ class AddProductsToCart
51
51
*/
52
52
private $ productRepository ;
53
53
54
- /**
55
- * @var array
56
- */
57
- private $ errors = [];
58
-
59
54
/**
60
55
* @var CartRepositoryInterface
61
56
*/
@@ -71,22 +66,30 @@ class AddProductsToCart
71
66
*/
72
67
private $ requestBuilder ;
73
68
69
+ /**
70
+ * @var \Magento\Quote\Model\QuoteFactory
71
+ */
72
+ private $ quoteFactory ;
73
+
74
74
/**
75
75
* @param ProductRepositoryInterface $productRepository
76
76
* @param CartRepositoryInterface $cartRepository
77
77
* @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
78
78
* @param BuyRequestBuilder $requestBuilder
79
+ * @param \Magento\Quote\Model\QuoteFactory $quoteFactory
79
80
*/
80
81
public function __construct (
81
82
ProductRepositoryInterface $ productRepository ,
82
83
CartRepositoryInterface $ cartRepository ,
83
84
MaskedQuoteIdToQuoteIdInterface $ maskedQuoteIdToQuoteId ,
84
- BuyRequestBuilder $ requestBuilder
85
+ BuyRequestBuilder $ requestBuilder ,
86
+ \Magento \Quote \Model \QuoteFactory $ quoteFactory
85
87
) {
86
88
$ this ->productRepository = $ productRepository ;
87
89
$ this ->cartRepository = $ cartRepository ;
88
90
$ this ->maskedQuoteIdToQuoteId = $ maskedQuoteIdToQuoteId ;
89
91
$ this ->requestBuilder = $ requestBuilder ;
92
+ $ this ->quoteFactory = $ quoteFactory ;
90
93
}
91
94
92
95
/**
@@ -101,33 +104,39 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa
101
104
{
102
105
$ cartId = $ this ->maskedQuoteIdToQuoteId ->execute ($ maskedCartId );
103
106
$ 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 = [];
116
108
if ($ cart ->getData ('has_error ' )) {
117
109
$ errors = $ cart ->getErrors ();
118
110
119
111
/** @var MessageInterface $error */
120
112
foreach ($ errors as $ error ) {
121
- $ this ->addError ($ error ->getText ());
113
+ $ allErrors [] = $ this ->createError ($ error ->getText ());
122
114
}
123
115
}
124
116
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 ) {
126
135
/* Revert changes introduced by add to cart processes in case of an error */
127
136
$ cart ->getItemsCollection ()->clear ();
128
137
}
129
138
130
- return $ this ->prepareErrorOutput ($ cart );
139
+ return $ this ->prepareErrorOutput ($ cart, $ allErrors );
131
140
}
132
141
133
142
/**
@@ -136,59 +145,59 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa
136
145
* @param CartInterface|Quote $cart
137
146
* @param Data\CartItem $cartItem
138
147
* @param int $cartItemPosition
148
+ * @return array
139
149
*/
140
- private function addItemToCart (CartInterface $ cart , Data \CartItem $ cartItem , int $ cartItemPosition ): void
150
+ private function addItemToCart (CartInterface $ cart , Data \CartItem $ cartItem , int $ cartItemPosition ): array
141
151
{
142
152
$ sku = $ cartItem ->getSku ();
153
+ $ errors = [];
143
154
144
155
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
+ }
160
167
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
+ }
173
178
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
+ }
178
185
}
179
186
}
187
+
188
+ return $ errors ;
180
189
}
181
190
182
191
/**
183
192
* Add order line item error
184
193
*
185
194
* @param string $message
186
195
* @param int $cartItemPosition
187
- * @return void
196
+ * @return Data\Error
188
197
*/
189
- private function addError (string $ message , int $ cartItemPosition = 0 ): void
198
+ private function createError (string $ message , int $ cartItemPosition = 0 ): Data \ Error
190
199
{
191
- $ this -> errors [] = new Data \Error (
200
+ return new Data \Error (
192
201
$ message ,
193
202
$ this ->getErrorCode ($ message ),
194
203
$ cartItemPosition
@@ -219,14 +228,53 @@ private function getErrorCode(string $message): string
219
228
* Creates a new output from existing errors
220
229
*
221
230
* @param CartInterface $cart
231
+ * @param array $errors
222
232
* @return AddProductsToCartOutput
223
233
*/
224
- private function prepareErrorOutput (CartInterface $ cart ): AddProductsToCartOutput
234
+ private function prepareErrorOutput (CartInterface $ cart, array $ errors = [] ): AddProductsToCartOutput
225
235
{
226
- $ output = new AddProductsToCartOutput ($ cart , $ this ->errors );
227
- $ this ->errors = [];
236
+ $ output = new AddProductsToCartOutput ($ cart , $ errors );
228
237
$ cart ->setHasError (false );
229
238
230
239
return $ output ;
231
240
}
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
+ }
232
280
}
0 commit comments