8
8
9
9
namespace Magento \Catalog \Model \Indexer \Category \Product ;
10
10
11
- use Magento \Framework \DB \Query \Generator as QueryGenerator ;
11
+ use Magento \Catalog \Api \Data \ProductInterface ;
12
+ use Magento \Catalog \Model \Product ;
13
+ use Magento \Framework \App \ObjectManager ;
12
14
use Magento \Framework \App \ResourceConnection ;
15
+ use Magento \Framework \DB \Query \Generator as QueryGenerator ;
16
+ use Magento \Framework \DB \Select ;
13
17
use Magento \Framework \EntityManager \MetadataPool ;
18
+ use Magento \Store \Model \Store ;
14
19
15
20
/**
16
21
* Class AbstractAction
@@ -41,21 +46,21 @@ abstract class AbstractAction
41
46
/**
42
47
* Cached non anchor categories select by store id
43
48
*
44
- * @var \Magento\Framework\DB\ Select[]
49
+ * @var Select[]
45
50
*/
46
51
protected $ nonAnchorSelects = [];
47
52
48
53
/**
49
54
* Cached anchor categories select by store id
50
55
*
51
- * @var \Magento\Framework\DB\ Select[]
56
+ * @var Select[]
52
57
*/
53
58
protected $ anchorSelects = [];
54
59
55
60
/**
56
61
* Cached all product select by store id
57
62
*
58
- * @var \Magento\Framework\DB\ Select[]
63
+ * @var Select[]
59
64
*/
60
65
protected $ productsSelects = [];
61
66
@@ -113,19 +118,21 @@ abstract class AbstractAction
113
118
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
114
119
* @param \Magento\Catalog\Model\Config $config
115
120
* @param QueryGenerator $queryGenerator
121
+ * @param MetadataPool|null $metadataPool
116
122
*/
117
123
public function __construct (
118
124
\Magento \Framework \App \ResourceConnection $ resource ,
119
125
\Magento \Store \Model \StoreManagerInterface $ storeManager ,
120
126
\Magento \Catalog \Model \Config $ config ,
121
- QueryGenerator $ queryGenerator = null
127
+ QueryGenerator $ queryGenerator = null ,
128
+ MetadataPool $ metadataPool = null
122
129
) {
123
130
$ this ->resource = $ resource ;
124
131
$ this ->connection = $ resource ->getConnection ();
125
132
$ this ->storeManager = $ storeManager ;
126
133
$ this ->config = $ config ;
127
- $ this ->queryGenerator = $ queryGenerator ?: \ Magento \ Framework \ App \ ObjectManager::getInstance ()
128
- ->get (QueryGenerator ::class);
134
+ $ this ->queryGenerator = $ queryGenerator ?: ObjectManager::getInstance ()-> get (QueryGenerator::class);
135
+ $ this -> metadataPool = $ metadataPool ?: ObjectManager:: getInstance () ->get (MetadataPool ::class);
129
136
}
130
137
131
138
/**
@@ -179,9 +186,9 @@ protected function getMainTable()
179
186
*/
180
187
protected function getMainTmpTable ()
181
188
{
182
- return $ this ->useTempTable ? $ this -> getTable (
183
- self ::MAIN_INDEX_TABLE . self ::TEMPORARY_TABLE_SUFFIX
184
- ) : $ this ->getMainTable ();
189
+ return $ this ->useTempTable
190
+ ? $ this -> getTable ( self ::MAIN_INDEX_TABLE . self ::TEMPORARY_TABLE_SUFFIX )
191
+ : $ this ->getMainTable ();
185
192
}
186
193
187
194
/**
@@ -209,24 +216,25 @@ protected function getPathFromCategoryId($categoryId)
209
216
/**
210
217
* Retrieve select for reindex products of non anchor categories
211
218
*
212
- * @param \Magento\Store\Model\Store $store
213
- * @return \Magento\Framework\DB\Select
219
+ * @param Store $store
220
+ * @return Select
221
+ * @throws \Exception when metadata not found for ProductInterface
214
222
*/
215
- protected function getNonAnchorCategoriesSelect (\ Magento \ Store \ Model \ Store $ store )
223
+ protected function getNonAnchorCategoriesSelect (Store $ store )
216
224
{
217
225
if (!isset ($ this ->nonAnchorSelects [$ store ->getId ()])) {
218
226
$ statusAttributeId = $ this ->config ->getAttribute (
219
- \ Magento \ Catalog \ Model \ Product::ENTITY ,
227
+ Product::ENTITY ,
220
228
'status '
221
229
)->getId ();
222
230
$ visibilityAttributeId = $ this ->config ->getAttribute (
223
- \ Magento \ Catalog \ Model \ Product::ENTITY ,
231
+ Product::ENTITY ,
224
232
'visibility '
225
233
)->getId ();
226
234
227
235
$ rootPath = $ this ->getPathFromCategoryId ($ store ->getRootCategoryId ());
228
236
229
- $ metadata = $ this ->getMetadataPool () ->getMetadata (\ Magento \ Catalog \ Api \ Data \ ProductInterface::class);
237
+ $ metadata = $ this ->metadataPool ->getMetadata (ProductInterface::class);
230
238
$ linkField = $ metadata ->getLinkField ();
231
239
$ select = $ this ->connection ->select ()->from (
232
240
['cc ' => $ this ->getTable ('catalog_category_entity ' )],
@@ -295,12 +303,65 @@ protected function getNonAnchorCategoriesSelect(\Magento\Store\Model\Store $stor
295
303
]
296
304
);
297
305
306
+ $ this ->addFilteringByChildProductsToSelect ($ select , $ store );
307
+
298
308
$ this ->nonAnchorSelects [$ store ->getId ()] = $ select ;
299
309
}
300
310
301
311
return $ this ->nonAnchorSelects [$ store ->getId ()];
302
312
}
303
313
314
+ /**
315
+ * Add filtering by child products to select
316
+ *
317
+ * It's used for correct handling of composite products.
318
+ * This method makes assumption that select already joins `catalog_product_entity` as `cpe`.
319
+ *
320
+ * @param Select $select
321
+ * @param Store $store
322
+ * @return void
323
+ * @throws \Exception when metadata not found for ProductInterface
324
+ */
325
+ private function addFilteringByChildProductsToSelect (Select $ select , Store $ store )
326
+ {
327
+ $ metadata = $ this ->metadataPool ->getMetadata (ProductInterface::class);
328
+ $ linkField = $ metadata ->getLinkField ();
329
+
330
+ $ statusAttributeId = $ this ->config ->getAttribute (Product::ENTITY , 'status ' )->getId ();
331
+
332
+ $ select ->joinLeft (
333
+ ['relation ' => $ this ->getTable ('catalog_product_relation ' )],
334
+ 'cpe. ' . $ linkField . ' = relation.parent_id ' ,
335
+ []
336
+ )->joinLeft (
337
+ ['relation_product_entity ' => $ this ->getTable ('catalog_product_entity ' )],
338
+ 'relation.child_id = relation_product_entity.entity_id ' ,
339
+ []
340
+ )->joinLeft (
341
+ ['child_cpsd ' => $ this ->getTable ('catalog_product_entity_int ' )],
342
+ 'child_cpsd. ' . $ linkField . ' = ' . 'relation_product_entity. ' . $ linkField
343
+ . ' AND child_cpsd.store_id = 0 '
344
+ . ' AND child_cpsd.attribute_id = ' . $ statusAttributeId ,
345
+ []
346
+ )->joinLeft (
347
+ ['child_cpss ' => $ this ->getTable ('catalog_product_entity_int ' )],
348
+ 'child_cpss. ' . $ linkField . ' = ' . 'relation_product_entity. ' . $ linkField . ''
349
+ . ' AND child_cpss.attribute_id = child_cpsd.attribute_id '
350
+ . ' AND child_cpss.store_id = ' . $ store ->getId (),
351
+ []
352
+ )->where (
353
+ 'relation.child_id IS NULL OR '
354
+ . $ this ->connection ->getIfNullSql ('child_cpss.value ' , 'child_cpsd.value ' ) . ' = ? ' ,
355
+ \Magento \Catalog \Model \Product \Attribute \Source \Status::STATUS_ENABLED
356
+ )->group (
357
+ [
358
+ 'cc.entity_id ' ,
359
+ 'ccp.product_id ' ,
360
+ 'visibility '
361
+ ]
362
+ );
363
+ }
364
+
304
365
/**
305
366
* Check whether select ranging is needed
306
367
*
@@ -314,16 +375,13 @@ protected function isRangingNeeded()
314
375
/**
315
376
* Return selects cut by min and max.
316
377
*
317
- * @param \Magento\Framework\DB\ Select $select
378
+ * @param Select $select
318
379
* @param string $field
319
380
* @param int $range
320
- * @return \Magento\Framework\DB\ Select[]
381
+ * @return Select[]
321
382
*/
322
- protected function prepareSelectsByRange (
323
- \Magento \Framework \DB \Select $ select ,
324
- $ field ,
325
- $ range = self ::RANGE_CATEGORY_STEP
326
- ) {
383
+ protected function prepareSelectsByRange (Select $ select , $ field , $ range = self ::RANGE_CATEGORY_STEP )
384
+ {
327
385
if ($ this ->isRangingNeeded ()) {
328
386
$ iterator = $ this ->queryGenerator ->generate (
329
387
$ field ,
@@ -346,10 +404,10 @@ protected function prepareSelectsByRange(
346
404
/**
347
405
* Reindex products of non anchor categories
348
406
*
349
- * @param \Magento\Store\Model\ Store $store
407
+ * @param Store $store
350
408
* @return void
351
409
*/
352
- protected function reindexNonAnchorCategories (\ Magento \ Store \ Model \ Store $ store )
410
+ protected function reindexNonAnchorCategories (Store $ store )
353
411
{
354
412
$ selects = $ this ->prepareSelectsByRange ($ this ->getNonAnchorCategoriesSelect ($ store ), 'entity_id ' );
355
413
foreach ($ selects as $ select ) {
@@ -367,43 +425,44 @@ protected function reindexNonAnchorCategories(\Magento\Store\Model\Store $store)
367
425
/**
368
426
* Check if anchor select isset
369
427
*
370
- * @param \Magento\Store\Model\ Store $store
428
+ * @param Store $store
371
429
* @return bool
372
430
*/
373
- protected function hasAnchorSelect (\ Magento \ Store \ Model \ Store $ store )
431
+ protected function hasAnchorSelect (Store $ store )
374
432
{
375
433
return isset ($ this ->anchorSelects [$ store ->getId ()]);
376
434
}
377
435
378
436
/**
379
437
* Create anchor select
380
438
*
381
- * @param \Magento\Store\Model\Store $store
382
- * @return \Magento\Framework\DB\Select
439
+ * @param Store $store
440
+ * @return Select
441
+ * @throws \Exception when metadata not found for ProductInterface or CategoryInterface
383
442
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
384
443
*/
385
- protected function createAnchorSelect (\ Magento \ Store \ Model \ Store $ store )
444
+ protected function createAnchorSelect (Store $ store )
386
445
{
387
446
$ isAnchorAttributeId = $ this ->config ->getAttribute (
388
447
\Magento \Catalog \Model \Category::ENTITY ,
389
448
'is_anchor '
390
449
)->getId ();
391
- $ statusAttributeId = $ this ->config ->getAttribute (\ Magento \ Catalog \ Model \ Product::ENTITY , 'status ' )->getId ();
450
+ $ statusAttributeId = $ this ->config ->getAttribute (Product::ENTITY , 'status ' )->getId ();
392
451
$ visibilityAttributeId = $ this ->config ->getAttribute (
393
- \ Magento \ Catalog \ Model \ Product::ENTITY ,
452
+ Product::ENTITY ,
394
453
'visibility '
395
454
)->getId ();
396
455
$ rootCatIds = explode ('/ ' , $ this ->getPathFromCategoryId ($ store ->getRootCategoryId ()));
397
456
array_pop ($ rootCatIds );
398
457
399
458
$ temporaryTreeTable = $ this ->makeTempCategoryTreeIndex ();
400
459
401
- $ productMetadata = $ this ->getMetadataPool () ->getMetadata (\ Magento \ Catalog \ Api \ Data \ ProductInterface::class);
402
- $ categoryMetadata = $ this ->getMetadataPool () ->getMetadata (\Magento \Catalog \Api \Data \CategoryInterface::class);
460
+ $ productMetadata = $ this ->metadataPool ->getMetadata (ProductInterface::class);
461
+ $ categoryMetadata = $ this ->metadataPool ->getMetadata (\Magento \Catalog \Api \Data \CategoryInterface::class);
403
462
$ productLinkField = $ productMetadata ->getLinkField ();
404
463
$ categoryLinkField = $ categoryMetadata ->getLinkField ();
405
464
406
- return $ this ->connection ->select ()->from (
465
+ $ select = $ this ->connection ->select ()->from (
407
466
['cc ' => $ this ->getTable ('catalog_category_entity ' )],
408
467
[]
409
468
)->joinInner (
@@ -485,6 +544,10 @@ protected function createAnchorSelect(\Magento\Store\Model\Store $store)
485
544
'visibility ' => new \Zend_Db_Expr ($ this ->connection ->getIfNullSql ('cpvs.value ' , 'cpvd.value ' )),
486
545
]
487
546
);
547
+
548
+ $ this ->addFilteringByChildProductsToSelect ($ select , $ store );
549
+
550
+ return $ select ;
488
551
}
489
552
490
553
/**
@@ -576,10 +639,10 @@ protected function fillTempCategoryTreeIndex($temporaryName)
576
639
/**
577
640
* Retrieve select for reindex products of non anchor categories
578
641
*
579
- * @param \Magento\Store\Model\ Store $store
580
- * @return \Magento\Framework\DB\ Select
642
+ * @param Store $store
643
+ * @return Select
581
644
*/
582
- protected function getAnchorCategoriesSelect (\ Magento \ Store \ Model \ Store $ store )
645
+ protected function getAnchorCategoriesSelect (Store $ store )
583
646
{
584
647
if (!$ this ->hasAnchorSelect ($ store )) {
585
648
$ this ->anchorSelects [$ store ->getId ()] = $ this ->createAnchorSelect ($ store );
@@ -590,10 +653,10 @@ protected function getAnchorCategoriesSelect(\Magento\Store\Model\Store $store)
590
653
/**
591
654
* Reindex products of anchor categories
592
655
*
593
- * @param \Magento\Store\Model\ Store $store
656
+ * @param Store $store
594
657
* @return void
595
658
*/
596
- protected function reindexAnchorCategories (\ Magento \ Store \ Model \ Store $ store )
659
+ protected function reindexAnchorCategories (Store $ store )
597
660
{
598
661
$ selects = $ this ->prepareSelectsByRange ($ this ->getAnchorCategoriesSelect ($ store ), 'entity_id ' );
599
662
@@ -612,22 +675,23 @@ protected function reindexAnchorCategories(\Magento\Store\Model\Store $store)
612
675
/**
613
676
* Get select for all products
614
677
*
615
- * @param \Magento\Store\Model\Store $store
616
- * @return \Magento\Framework\DB\Select
678
+ * @param Store $store
679
+ * @return Select
680
+ * @throws \Exception when metadata not found for ProductInterface
617
681
*/
618
- protected function getAllProducts (\ Magento \ Store \ Model \ Store $ store )
682
+ protected function getAllProducts (Store $ store )
619
683
{
620
684
if (!isset ($ this ->productsSelects [$ store ->getId ()])) {
621
685
$ statusAttributeId = $ this ->config ->getAttribute (
622
- \ Magento \ Catalog \ Model \ Product::ENTITY ,
686
+ Product::ENTITY ,
623
687
'status '
624
688
)->getId ();
625
689
$ visibilityAttributeId = $ this ->config ->getAttribute (
626
- \ Magento \ Catalog \ Model \ Product::ENTITY ,
690
+ Product::ENTITY ,
627
691
'visibility '
628
692
)->getId ();
629
693
630
- $ metadata = $ this ->getMetadataPool () ->getMetadata (\ Magento \ Catalog \ Api \ Data \ ProductInterface::class);
694
+ $ metadata = $ this ->metadataPool ->getMetadata (ProductInterface::class);
631
695
$ linkField = $ metadata ->getLinkField ();
632
696
633
697
$ select = $ this ->connection ->select ()->from (
@@ -716,10 +780,10 @@ protected function isIndexRootCategoryNeeded()
716
780
/**
717
781
* Reindex all products to root category
718
782
*
719
- * @param \Magento\Store\Model\ Store $store
783
+ * @param Store $store
720
784
* @return void
721
785
*/
722
- protected function reindexRootCategory (\ Magento \ Store \ Model \ Store $ store )
786
+ protected function reindexRootCategory (Store $ store )
723
787
{
724
788
if ($ this ->isIndexRootCategoryNeeded ()) {
725
789
$ selects = $ this ->prepareSelectsByRange (
@@ -740,16 +804,4 @@ protected function reindexRootCategory(\Magento\Store\Model\Store $store)
740
804
}
741
805
}
742
806
}
743
-
744
- /**
745
- * @return \Magento\Framework\EntityManager\MetadataPool
746
- */
747
- private function getMetadataPool ()
748
- {
749
- if (null === $ this ->metadataPool ) {
750
- $ this ->metadataPool = \Magento \Framework \App \ObjectManager::getInstance ()
751
- ->get ('Magento\Framework\EntityManager\MetadataPool ' );
752
- }
753
- return $ this ->metadataPool ;
754
- }
755
807
}
0 commit comments