3
3
* Copyright © Magento, Inc. All rights reserved.
4
4
* See COPYING.txt for license details.
5
5
*/
6
+ declare (strict_types=1 );
7
+
6
8
namespace Magento \Quote \Model \ResourceModel \Quote \Item ;
7
9
8
- use \Magento \Catalog \Model \ResourceModel \Product \Collection as ProductCollection ;
10
+ use Magento \Catalog \Api \Data \ProductInterface ;
11
+ use Magento \Catalog \Model \ResourceModel \Product \Collection as ProductCollection ;
12
+ use Magento \Catalog \Model \Product \Attribute \Source \Status as ProductStatus ;
13
+ use Magento \Quote \Model \Quote ;
14
+ use Magento \Quote \Model \Quote \Item as QuoteItem ;
15
+ use Magento \Quote \Model \ResourceModel \Quote \Item as ResourceQuoteItem ;
9
16
10
17
/**
11
18
* Quote item resource collection
@@ -50,6 +57,11 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\VersionContro
50
57
*/
51
58
private $ storeManager ;
52
59
60
+ /**
61
+ * @var bool $recollectQuote
62
+ */
63
+ private $ recollectQuote = false ;
64
+
53
65
/**
54
66
* @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
55
67
* @param \Psr\Log\LoggerInterface $logger
@@ -102,15 +114,15 @@ public function __construct(
102
114
*/
103
115
protected function _construct ()
104
116
{
105
- $ this ->_init (\ Magento \ Quote \ Model \ Quote \Item ::class, \ Magento \ Quote \ Model \ ResourceModel \ Quote \Item ::class);
117
+ $ this ->_init (QuoteItem ::class, ResourceQuoteItem ::class);
106
118
}
107
119
108
120
/**
109
121
* Retrieve store Id (From Quote)
110
122
*
111
123
* @return int
112
124
*/
113
- public function getStoreId ()
125
+ public function getStoreId (): int
114
126
{
115
127
// Fallback to current storeId if no quote is provided
116
128
// (see https://github.com/magento/magento2/commit/9d3be732a88884a66d667b443b3dc1655ddd0721)
@@ -119,12 +131,12 @@ public function getStoreId()
119
131
}
120
132
121
133
/**
122
- * Set Quote object to Collection
134
+ * Set Quote object to Collection.
123
135
*
124
- * @param \Magento\Quote\Model\ Quote $quote
136
+ * @param Quote $quote
125
137
* @return $this
126
138
*/
127
- public function setQuote ($ quote )
139
+ public function setQuote ($ quote ): self
128
140
{
129
141
$ this ->_quote = $ quote ;
130
142
$ quoteId = $ quote ->getId ();
@@ -138,13 +150,15 @@ public function setQuote($quote)
138
150
}
139
151
140
152
/**
141
- * Reset the collection and join it to quotes table. Optionally can select items with specified product id only.
153
+ * Reset the collection and inner join it to quotes table.
154
+ *
155
+ * Optionally can select items with specified product id only
142
156
*
143
157
* @param string $quotesTableName
144
158
* @param int $productId
145
159
* @return $this
146
160
*/
147
- public function resetJoinQuotes ($ quotesTableName , $ productId = null )
161
+ public function resetJoinQuotes ($ quotesTableName , $ productId = null ): self
148
162
{
149
163
$ this ->getSelect ()->reset ()->from (
150
164
['qi ' => $ this ->getResource ()->getMainTable ()],
@@ -161,11 +175,11 @@ public function resetJoinQuotes($quotesTableName, $productId = null)
161
175
}
162
176
163
177
/**
164
- * After load processing
178
+ * After load processing.
165
179
*
166
180
* @return $this
167
181
*/
168
- protected function _afterLoad ()
182
+ protected function _afterLoad (): self
169
183
{
170
184
parent ::_afterLoad ();
171
185
@@ -194,11 +208,11 @@ protected function _afterLoad()
194
208
}
195
209
196
210
/**
197
- * Add options to items
211
+ * Add options to items.
198
212
*
199
213
* @return $this
200
214
*/
201
- protected function _assignOptions ()
215
+ protected function _assignOptions (): self
202
216
{
203
217
$ itemIds = array_keys ($ this ->_items );
204
218
$ optionCollection = $ this ->_itemOptionCollectionFactory ->create ()->addItemFilter ($ itemIds );
@@ -212,12 +226,12 @@ protected function _assignOptions()
212
226
}
213
227
214
228
/**
215
- * Add products to items and item options
229
+ * Add products to items and item options.
216
230
*
217
231
* @return $this
218
232
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
219
233
*/
220
- protected function _assignProducts ()
234
+ protected function _assignProducts (): self
221
235
{
222
236
\Magento \Framework \Profiler::start ('QUOTE: ' . __METHOD__ , ['group ' => 'QUOTE ' , 'method ' => __METHOD__ ]);
223
237
$ productCollection = $ this ->_productCollectionFactory ->create ()->setStoreId (
@@ -239,53 +253,88 @@ protected function _assignProducts()
239
253
['collection ' => $ productCollection ]
240
254
);
241
255
242
- $ recollectQuote = false ;
243
256
foreach ($ this as $ item ) {
257
+ /** @var ProductInterface $product */
244
258
$ product = $ productCollection ->getItemById ($ item ->getProductId ());
245
- if ($ product ) {
259
+ $ isValidProduct = $ this ->isValidProduct ($ product );
260
+ $ qtyOptions = [];
261
+ if ($ isValidProduct ) {
246
262
$ product ->setCustomOptions ([]);
247
- $ qtyOptions = [];
248
- $ optionProductIds = [];
249
- foreach ($ item ->getOptions () as $ option ) {
250
- /**
251
- * Call type-specific logic for product associated with quote item
252
- */
253
- $ product ->getTypeInstance ()->assignProductToOption (
254
- $ productCollection ->getItemById ($ option ->getProductId ()),
255
- $ option ,
256
- $ product
257
- );
258
-
259
- if (is_object ($ option ->getProduct ()) && $ option ->getProduct ()->getId () != $ product ->getId ()) {
260
- $ optionProductIds [$ option ->getProduct ()->getId ()] = $ option ->getProduct ()->getId ();
261
- }
262
- }
263
-
264
- if ($ optionProductIds ) {
265
- foreach ($ optionProductIds as $ optionProductId ) {
266
- $ qtyOption = $ item ->getOptionByCode ('product_qty_ ' . $ optionProductId );
267
- if ($ qtyOption ) {
268
- $ qtyOptions [$ optionProductId ] = $ qtyOption ;
269
- }
263
+ $ optionProductIds = $ this ->getOptionProductIds ($ item , $ product , $ productCollection );
264
+ foreach ($ optionProductIds as $ optionProductId ) {
265
+ $ qtyOption = $ item ->getOptionByCode ('product_qty_ ' . $ optionProductId );
266
+ if ($ qtyOption ) {
267
+ $ qtyOptions [$ optionProductId ] = $ qtyOption ;
270
268
}
271
269
}
272
-
273
- $ item ->setQtyOptions ($ qtyOptions )->setProduct ($ product );
274
270
} else {
275
271
$ item ->isDeleted (true );
276
- $ recollectQuote = true ;
272
+ $ this ->recollectQuote = true ;
273
+ }
274
+ if (!$ item ->isDeleted ()) {
275
+ $ item ->setQtyOptions ($ qtyOptions )->setProduct ($ product );
276
+ $ item ->checkData ();
277
277
}
278
- $ item ->checkData ();
279
278
}
280
-
281
- if ($ recollectQuote && $ this ->_quote ) {
279
+ if ($ this ->recollectQuote && $ this ->_quote ) {
282
280
$ this ->_quote ->collectTotals ();
283
281
}
284
282
\Magento \Framework \Profiler::stop ('QUOTE: ' . __METHOD__ );
285
283
286
284
return $ this ;
287
285
}
288
286
287
+ /**
288
+ * Get product Ids from option.
289
+ *
290
+ * @param QuoteItem $item
291
+ * @param ProductInterface $product
292
+ * @param ProductCollection $productCollection
293
+ * @return array
294
+ */
295
+ private function getOptionProductIds (
296
+ QuoteItem $ item ,
297
+ ProductInterface $ product ,
298
+ ProductCollection $ productCollection
299
+ ): array {
300
+ $ optionProductIds = [];
301
+ foreach ($ item ->getOptions () as $ option ) {
302
+ /**
303
+ * Call type-specific logic for product associated with quote item
304
+ */
305
+ $ product ->getTypeInstance ()->assignProductToOption (
306
+ $ productCollection ->getItemById ($ option ->getProductId ()),
307
+ $ option ,
308
+ $ product
309
+ );
310
+
311
+ if (is_object ($ option ->getProduct ()) && $ option ->getProduct ()->getId () != $ product ->getId ()) {
312
+ $ isValidProduct = $ this ->isValidProduct ($ option ->getProduct ());
313
+ if (!$ isValidProduct && !$ item ->isDeleted ()) {
314
+ $ item ->isDeleted (true );
315
+ $ this ->recollectQuote = true ;
316
+ continue ;
317
+ }
318
+ $ optionProductIds [$ option ->getProduct ()->getId ()] = $ option ->getProduct ()->getId ();
319
+ }
320
+ }
321
+
322
+ return $ optionProductIds ;
323
+ }
324
+
325
+ /**
326
+ * Check is valid product.
327
+ *
328
+ * @param ProductInterface $product
329
+ * @return bool
330
+ */
331
+ private function isValidProduct (ProductInterface $ product ): bool
332
+ {
333
+ $ result = ($ product && (int )$ product ->getStatus () !== ProductStatus::STATUS_DISABLED );
334
+
335
+ return $ result ;
336
+ }
337
+
289
338
/**
290
339
* Prevents adding stock status filter to the collection of products.
291
340
*
@@ -294,7 +343,7 @@ protected function _assignProducts()
294
343
*
295
344
* @see \Magento\CatalogInventory\Helper\Stock::addIsInStockFilterToCollection
296
345
*/
297
- private function skipStockStatusFilter (ProductCollection $ productCollection )
346
+ private function skipStockStatusFilter (ProductCollection $ productCollection ): void
298
347
{
299
348
$ productCollection ->setFlag ('has_stock_status_filter ' , true );
300
349
}
@@ -304,7 +353,7 @@ private function skipStockStatusFilter(ProductCollection $productCollection)
304
353
*
305
354
* @return void
306
355
*/
307
- private function removeItemsWithAbsentProducts ()
356
+ private function removeItemsWithAbsentProducts (): void
308
357
{
309
358
if (count ($ this ->_productIds ) === 0 ) {
310
359
return ;
0 commit comments