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
@@ -45,21 +50,21 @@ abstract class AbstractAction
45
50
/**
46
51
* Cached non anchor categories select by store id
47
52
*
48
- * @var \Magento\Framework\DB\ Select[]
53
+ * @var Select[]
49
54
*/
50
55
protected $ nonAnchorSelects = [];
51
56
52
57
/**
53
58
* Cached anchor categories select by store id
54
59
*
55
- * @var \Magento\Framework\DB\ Select[]
60
+ * @var Select[]
56
61
*/
57
62
protected $ anchorSelects = [];
58
63
59
64
/**
60
65
* Cached all product select by store id
61
66
*
62
- * @var \Magento\Framework\DB\ Select[]
67
+ * @var Select[]
63
68
*/
64
69
protected $ productsSelects = [];
65
70
@@ -119,19 +124,21 @@ abstract class AbstractAction
119
124
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
120
125
* @param \Magento\Catalog\Model\Config $config
121
126
* @param QueryGenerator $queryGenerator
127
+ * @param MetadataPool|null $metadataPool
122
128
*/
123
129
public function __construct (
124
130
\Magento \Framework \App \ResourceConnection $ resource ,
125
131
\Magento \Store \Model \StoreManagerInterface $ storeManager ,
126
132
\Magento \Catalog \Model \Config $ config ,
127
- QueryGenerator $ queryGenerator = null
133
+ QueryGenerator $ queryGenerator = null ,
134
+ MetadataPool $ metadataPool = null
128
135
) {
129
136
$ this ->resource = $ resource ;
130
137
$ this ->connection = $ resource ->getConnection ();
131
138
$ this ->storeManager = $ storeManager ;
132
139
$ this ->config = $ config ;
133
- $ this ->queryGenerator = $ queryGenerator ?: \ Magento \ Framework \ App \ ObjectManager::getInstance ()
134
- ->get (QueryGenerator ::class);
140
+ $ this ->queryGenerator = $ queryGenerator ?: ObjectManager::getInstance ()-> get (QueryGenerator::class);
141
+ $ this -> metadataPool = $ metadataPool ?: ObjectManager:: getInstance () ->get (MetadataPool ::class);
135
142
}
136
143
137
144
/**
@@ -188,9 +195,9 @@ protected function getMainTable()
188
195
*/
189
196
protected function getMainTmpTable ()
190
197
{
191
- return $ this ->useTempTable ? $ this -> getTable (
192
- self ::MAIN_INDEX_TABLE . self ::TEMPORARY_TABLE_SUFFIX
193
- ) : $ this ->getMainTable ();
198
+ return $ this ->useTempTable
199
+ ? $ this -> getTable ( self ::MAIN_INDEX_TABLE . self ::TEMPORARY_TABLE_SUFFIX )
200
+ : $ this ->getMainTable ();
194
201
}
195
202
196
203
/**
@@ -218,24 +225,25 @@ protected function getPathFromCategoryId($categoryId)
218
225
/**
219
226
* Retrieve select for reindex products of non anchor categories
220
227
*
221
- * @param \Magento\Store\Model\Store $store
222
- * @return \Magento\Framework\DB\Select
228
+ * @param Store $store
229
+ * @return Select
230
+ * @throws \Exception when metadata not found for ProductInterface
223
231
*/
224
- protected function getNonAnchorCategoriesSelect (\ Magento \ Store \ Model \ Store $ store )
232
+ protected function getNonAnchorCategoriesSelect (Store $ store )
225
233
{
226
234
if (!isset ($ this ->nonAnchorSelects [$ store ->getId ()])) {
227
235
$ statusAttributeId = $ this ->config ->getAttribute (
228
- \ Magento \ Catalog \ Model \ Product::ENTITY ,
236
+ Product::ENTITY ,
229
237
'status '
230
238
)->getId ();
231
239
$ visibilityAttributeId = $ this ->config ->getAttribute (
232
- \ Magento \ Catalog \ Model \ Product::ENTITY ,
240
+ Product::ENTITY ,
233
241
'visibility '
234
242
)->getId ();
235
243
236
244
$ rootPath = $ this ->getPathFromCategoryId ($ store ->getRootCategoryId ());
237
245
238
- $ metadata = $ this ->getMetadataPool () ->getMetadata (\ Magento \ Catalog \ Api \ Data \ ProductInterface::class);
246
+ $ metadata = $ this ->metadataPool ->getMetadata (ProductInterface::class);
239
247
$ linkField = $ metadata ->getLinkField ();
240
248
$ select = $ this ->connection ->select ()->from (
241
249
['cc ' => $ this ->getTable ('catalog_category_entity ' )],
@@ -304,12 +312,65 @@ protected function getNonAnchorCategoriesSelect(\Magento\Store\Model\Store $stor
304
312
]
305
313
);
306
314
315
+ $ this ->addFilteringByChildProductsToSelect ($ select , $ store );
316
+
307
317
$ this ->nonAnchorSelects [$ store ->getId ()] = $ select ;
308
318
}
309
319
310
320
return $ this ->nonAnchorSelects [$ store ->getId ()];
311
321
}
312
322
323
+ /**
324
+ * Add filtering by child products to select
325
+ *
326
+ * It's used for correct handling of composite products.
327
+ * This method makes assumption that select already joins `catalog_product_entity` as `cpe`.
328
+ *
329
+ * @param Select $select
330
+ * @param Store $store
331
+ * @return void
332
+ * @throws \Exception when metadata not found for ProductInterface
333
+ */
334
+ private function addFilteringByChildProductsToSelect (Select $ select , Store $ store )
335
+ {
336
+ $ metadata = $ this ->metadataPool ->getMetadata (ProductInterface::class);
337
+ $ linkField = $ metadata ->getLinkField ();
338
+
339
+ $ statusAttributeId = $ this ->config ->getAttribute (Product::ENTITY , 'status ' )->getId ();
340
+
341
+ $ select ->joinLeft (
342
+ ['relation ' => $ this ->getTable ('catalog_product_relation ' )],
343
+ 'cpe. ' . $ linkField . ' = relation.parent_id ' ,
344
+ []
345
+ )->joinLeft (
346
+ ['relation_product_entity ' => $ this ->getTable ('catalog_product_entity ' )],
347
+ 'relation.child_id = relation_product_entity.entity_id ' ,
348
+ []
349
+ )->joinLeft (
350
+ ['child_cpsd ' => $ this ->getTable ('catalog_product_entity_int ' )],
351
+ 'child_cpsd. ' . $ linkField . ' = ' . 'relation_product_entity. ' . $ linkField
352
+ . ' AND child_cpsd.store_id = 0 '
353
+ . ' AND child_cpsd.attribute_id = ' . $ statusAttributeId ,
354
+ []
355
+ )->joinLeft (
356
+ ['child_cpss ' => $ this ->getTable ('catalog_product_entity_int ' )],
357
+ 'child_cpss. ' . $ linkField . ' = ' . 'relation_product_entity. ' . $ linkField . ''
358
+ . ' AND child_cpss.attribute_id = child_cpsd.attribute_id '
359
+ . ' AND child_cpss.store_id = ' . $ store ->getId (),
360
+ []
361
+ )->where (
362
+ 'relation.child_id IS NULL OR '
363
+ . $ this ->connection ->getIfNullSql ('child_cpss.value ' , 'child_cpsd.value ' ) . ' = ? ' ,
364
+ \Magento \Catalog \Model \Product \Attribute \Source \Status::STATUS_ENABLED
365
+ )->group (
366
+ [
367
+ 'cc.entity_id ' ,
368
+ 'ccp.product_id ' ,
369
+ 'visibility '
370
+ ]
371
+ );
372
+ }
373
+
313
374
/**
314
375
* Check whether select ranging is needed
315
376
*
@@ -323,16 +384,13 @@ protected function isRangingNeeded()
323
384
/**
324
385
* Return selects cut by min and max
325
386
*
326
- * @param \Magento\Framework\DB\ Select $select
387
+ * @param Select $select
327
388
* @param string $field
328
389
* @param int $range
329
- * @return \Magento\Framework\DB\ Select[]
390
+ * @return Select[]
330
391
*/
331
- protected function prepareSelectsByRange (
332
- \Magento \Framework \DB \Select $ select ,
333
- $ field ,
334
- $ range = self ::RANGE_CATEGORY_STEP
335
- ) {
392
+ protected function prepareSelectsByRange (Select $ select , $ field , $ range = self ::RANGE_CATEGORY_STEP )
393
+ {
336
394
if ($ this ->isRangingNeeded ()) {
337
395
$ iterator = $ this ->queryGenerator ->generate (
338
396
$ field ,
@@ -353,10 +411,10 @@ protected function prepareSelectsByRange(
353
411
/**
354
412
* Reindex products of non anchor categories
355
413
*
356
- * @param \Magento\Store\Model\ Store $store
414
+ * @param Store $store
357
415
* @return void
358
416
*/
359
- protected function reindexNonAnchorCategories (\ Magento \ Store \ Model \ Store $ store )
417
+ protected function reindexNonAnchorCategories (Store $ store )
360
418
{
361
419
$ selects = $ this ->prepareSelectsByRange ($ this ->getNonAnchorCategoriesSelect ($ store ), 'entity_id ' );
362
420
foreach ($ selects as $ select ) {
@@ -374,43 +432,44 @@ protected function reindexNonAnchorCategories(\Magento\Store\Model\Store $store)
374
432
/**
375
433
* Check if anchor select isset
376
434
*
377
- * @param \Magento\Store\Model\ Store $store
435
+ * @param Store $store
378
436
* @return bool
379
437
*/
380
- protected function hasAnchorSelect (\ Magento \ Store \ Model \ Store $ store )
438
+ protected function hasAnchorSelect (Store $ store )
381
439
{
382
440
return isset ($ this ->anchorSelects [$ store ->getId ()]);
383
441
}
384
442
385
443
/**
386
444
* Create anchor select
387
445
*
388
- * @param \Magento\Store\Model\Store $store
389
- * @return \Magento\Framework\DB\Select
446
+ * @param Store $store
447
+ * @return Select
448
+ * @throws \Exception when metadata not found for ProductInterface or CategoryInterface
390
449
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
391
450
*/
392
- protected function createAnchorSelect (\ Magento \ Store \ Model \ Store $ store )
451
+ protected function createAnchorSelect (Store $ store )
393
452
{
394
453
$ isAnchorAttributeId = $ this ->config ->getAttribute (
395
454
\Magento \Catalog \Model \Category::ENTITY ,
396
455
'is_anchor '
397
456
)->getId ();
398
- $ statusAttributeId = $ this ->config ->getAttribute (\ Magento \ Catalog \ Model \ Product::ENTITY , 'status ' )->getId ();
457
+ $ statusAttributeId = $ this ->config ->getAttribute (Product::ENTITY , 'status ' )->getId ();
399
458
$ visibilityAttributeId = $ this ->config ->getAttribute (
400
- \ Magento \ Catalog \ Model \ Product::ENTITY ,
459
+ Product::ENTITY ,
401
460
'visibility '
402
461
)->getId ();
403
462
$ rootCatIds = explode ('/ ' , $ this ->getPathFromCategoryId ($ store ->getRootCategoryId ()));
404
463
array_pop ($ rootCatIds );
405
464
406
465
$ temporaryTreeTable = $ this ->makeTempCategoryTreeIndex ();
407
466
408
- $ productMetadata = $ this ->getMetadataPool () ->getMetadata (\ Magento \ Catalog \ Api \ Data \ ProductInterface::class);
409
- $ categoryMetadata = $ this ->getMetadataPool () ->getMetadata (\Magento \Catalog \Api \Data \CategoryInterface::class);
467
+ $ productMetadata = $ this ->metadataPool ->getMetadata (ProductInterface::class);
468
+ $ categoryMetadata = $ this ->metadataPool ->getMetadata (\Magento \Catalog \Api \Data \CategoryInterface::class);
410
469
$ productLinkField = $ productMetadata ->getLinkField ();
411
470
$ categoryLinkField = $ categoryMetadata ->getLinkField ();
412
471
413
- return $ this ->connection ->select ()->from (
472
+ $ select = $ this ->connection ->select ()->from (
414
473
['cc ' => $ this ->getTable ('catalog_category_entity ' )],
415
474
[]
416
475
)->joinInner (
@@ -492,6 +551,10 @@ protected function createAnchorSelect(\Magento\Store\Model\Store $store)
492
551
'visibility ' => new \Zend_Db_Expr ($ this ->connection ->getIfNullSql ('cpvs.value ' , 'cpvd.value ' )),
493
552
]
494
553
);
554
+
555
+ $ this ->addFilteringByChildProductsToSelect ($ select , $ store );
556
+
557
+ return $ select ;
495
558
}
496
559
497
560
/**
@@ -586,10 +649,10 @@ protected function fillTempCategoryTreeIndex($temporaryName)
586
649
/**
587
650
* Retrieve select for reindex products of non anchor categories
588
651
*
589
- * @param \Magento\Store\Model\ Store $store
590
- * @return \Magento\Framework\DB\ Select
652
+ * @param Store $store
653
+ * @return Select
591
654
*/
592
- protected function getAnchorCategoriesSelect (\ Magento \ Store \ Model \ Store $ store )
655
+ protected function getAnchorCategoriesSelect (Store $ store )
593
656
{
594
657
if (!$ this ->hasAnchorSelect ($ store )) {
595
658
$ this ->anchorSelects [$ store ->getId ()] = $ this ->createAnchorSelect ($ store );
@@ -600,10 +663,10 @@ protected function getAnchorCategoriesSelect(\Magento\Store\Model\Store $store)
600
663
/**
601
664
* Reindex products of anchor categories
602
665
*
603
- * @param \Magento\Store\Model\ Store $store
666
+ * @param Store $store
604
667
* @return void
605
668
*/
606
- protected function reindexAnchorCategories (\ Magento \ Store \ Model \ Store $ store )
669
+ protected function reindexAnchorCategories (Store $ store )
607
670
{
608
671
$ selects = $ this ->prepareSelectsByRange ($ this ->getAnchorCategoriesSelect ($ store ), 'entity_id ' );
609
672
@@ -622,22 +685,23 @@ protected function reindexAnchorCategories(\Magento\Store\Model\Store $store)
622
685
/**
623
686
* Get select for all products
624
687
*
625
- * @param \Magento\Store\Model\Store $store
626
- * @return \Magento\Framework\DB\Select
688
+ * @param Store $store
689
+ * @return Select
690
+ * @throws \Exception when metadata not found for ProductInterface
627
691
*/
628
- protected function getAllProducts (\ Magento \ Store \ Model \ Store $ store )
692
+ protected function getAllProducts (Store $ store )
629
693
{
630
694
if (!isset ($ this ->productsSelects [$ store ->getId ()])) {
631
695
$ statusAttributeId = $ this ->config ->getAttribute (
632
- \ Magento \ Catalog \ Model \ Product::ENTITY ,
696
+ Product::ENTITY ,
633
697
'status '
634
698
)->getId ();
635
699
$ visibilityAttributeId = $ this ->config ->getAttribute (
636
- \ Magento \ Catalog \ Model \ Product::ENTITY ,
700
+ Product::ENTITY ,
637
701
'visibility '
638
702
)->getId ();
639
703
640
- $ metadata = $ this ->getMetadataPool () ->getMetadata (\ Magento \ Catalog \ Api \ Data \ ProductInterface::class);
704
+ $ metadata = $ this ->metadataPool ->getMetadata (ProductInterface::class);
641
705
$ linkField = $ metadata ->getLinkField ();
642
706
643
707
$ select = $ this ->connection ->select ()->from (
@@ -726,10 +790,10 @@ protected function isIndexRootCategoryNeeded()
726
790
/**
727
791
* Reindex all products to root category
728
792
*
729
- * @param \Magento\Store\Model\ Store $store
793
+ * @param Store $store
730
794
* @return void
731
795
*/
732
- protected function reindexRootCategory (\ Magento \ Store \ Model \ Store $ store )
796
+ protected function reindexRootCategory (Store $ store )
733
797
{
734
798
if ($ this ->isIndexRootCategoryNeeded ()) {
735
799
$ selects = $ this ->prepareSelectsByRange (
@@ -750,16 +814,4 @@ protected function reindexRootCategory(\Magento\Store\Model\Store $store)
750
814
}
751
815
}
752
816
}
753
-
754
- /**
755
- * @return \Magento\Framework\EntityManager\MetadataPool
756
- */
757
- private function getMetadataPool ()
758
- {
759
- if (null === $ this ->metadataPool ) {
760
- $ this ->metadataPool = \Magento \Framework \App \ObjectManager::getInstance ()
761
- ->get (\Magento \Framework \EntityManager \MetadataPool::class);
762
- }
763
- return $ this ->metadataPool ;
764
- }
765
817
}
0 commit comments