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