Skip to content

Commit a0b5460

Browse files
Merge branch '2.2-develop' of github.com:magento/magento2ce into 8410
2 parents f728923 + d865ef1 commit a0b5460

File tree

391 files changed

+5900
-5267
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

391 files changed

+5900
-5267
lines changed

app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public function testBeforeSave($value, $errorMessage = null)
2020
\Magento\Backend\Model\Config\SessionLifetime\BackendModel::class
2121
);
2222
if ($errorMessage !== null) {
23-
$this->expectException(\Magento\Framework\Exception\LocalizedException::class, $errorMessage);
23+
$this->expectException(\Magento\Framework\Exception\LocalizedException::class);
24+
$this->expectExceptionMessage($errorMessage);
2425
}
2526
$model->setValue($value);
2627
$object = $model->beforeSave();

app/code/Magento/Catalog/Model/Category.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -943,8 +943,11 @@ public function getAnchorsAbove()
943943
*/
944944
public function getProductCount()
945945
{
946-
$count = $this->_getResource()->getProductCount($this);
947-
$this->setData(self::KEY_PRODUCT_COUNT, $count);
946+
if (!$this->hasData(self::KEY_PRODUCT_COUNT)) {
947+
$count = $this->_getResource()->getProductCount($this);
948+
$this->setData(self::KEY_PRODUCT_COUNT, $count);
949+
}
950+
948951
return $this->getData(self::KEY_PRODUCT_COUNT);
949952
}
950953

app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php

Lines changed: 112 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88

99
namespace Magento\Catalog\Model\Indexer\Category\Product;
1010

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;
1214
use Magento\Framework\App\ResourceConnection;
15+
use Magento\Framework\DB\Query\Generator as QueryGenerator;
16+
use Magento\Framework\DB\Select;
1317
use Magento\Framework\EntityManager\MetadataPool;
18+
use Magento\Store\Model\Store;
1419

1520
/**
1621
* Class AbstractAction
@@ -45,21 +50,21 @@ abstract class AbstractAction
4550
/**
4651
* Cached non anchor categories select by store id
4752
*
48-
* @var \Magento\Framework\DB\Select[]
53+
* @var Select[]
4954
*/
5055
protected $nonAnchorSelects = [];
5156

5257
/**
5358
* Cached anchor categories select by store id
5459
*
55-
* @var \Magento\Framework\DB\Select[]
60+
* @var Select[]
5661
*/
5762
protected $anchorSelects = [];
5863

5964
/**
6065
* Cached all product select by store id
6166
*
62-
* @var \Magento\Framework\DB\Select[]
67+
* @var Select[]
6368
*/
6469
protected $productsSelects = [];
6570

@@ -119,19 +124,21 @@ abstract class AbstractAction
119124
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
120125
* @param \Magento\Catalog\Model\Config $config
121126
* @param QueryGenerator $queryGenerator
127+
* @param MetadataPool|null $metadataPool
122128
*/
123129
public function __construct(
124130
\Magento\Framework\App\ResourceConnection $resource,
125131
\Magento\Store\Model\StoreManagerInterface $storeManager,
126132
\Magento\Catalog\Model\Config $config,
127-
QueryGenerator $queryGenerator = null
133+
QueryGenerator $queryGenerator = null,
134+
MetadataPool $metadataPool = null
128135
) {
129136
$this->resource = $resource;
130137
$this->connection = $resource->getConnection();
131138
$this->storeManager = $storeManager;
132139
$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);
135142
}
136143

137144
/**
@@ -188,9 +195,9 @@ protected function getMainTable()
188195
*/
189196
protected function getMainTmpTable()
190197
{
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();
194201
}
195202

196203
/**
@@ -218,24 +225,25 @@ protected function getPathFromCategoryId($categoryId)
218225
/**
219226
* Retrieve select for reindex products of non anchor categories
220227
*
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
223231
*/
224-
protected function getNonAnchorCategoriesSelect(\Magento\Store\Model\Store $store)
232+
protected function getNonAnchorCategoriesSelect(Store $store)
225233
{
226234
if (!isset($this->nonAnchorSelects[$store->getId()])) {
227235
$statusAttributeId = $this->config->getAttribute(
228-
\Magento\Catalog\Model\Product::ENTITY,
236+
Product::ENTITY,
229237
'status'
230238
)->getId();
231239
$visibilityAttributeId = $this->config->getAttribute(
232-
\Magento\Catalog\Model\Product::ENTITY,
240+
Product::ENTITY,
233241
'visibility'
234242
)->getId();
235243

236244
$rootPath = $this->getPathFromCategoryId($store->getRootCategoryId());
237245

238-
$metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
246+
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
239247
$linkField = $metadata->getLinkField();
240248
$select = $this->connection->select()->from(
241249
['cc' => $this->getTable('catalog_category_entity')],
@@ -304,12 +312,65 @@ protected function getNonAnchorCategoriesSelect(\Magento\Store\Model\Store $stor
304312
]
305313
);
306314

315+
$this->addFilteringByChildProductsToSelect($select, $store);
316+
307317
$this->nonAnchorSelects[$store->getId()] = $select;
308318
}
309319

310320
return $this->nonAnchorSelects[$store->getId()];
311321
}
312322

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+
313374
/**
314375
* Check whether select ranging is needed
315376
*
@@ -323,16 +384,13 @@ protected function isRangingNeeded()
323384
/**
324385
* Return selects cut by min and max
325386
*
326-
* @param \Magento\Framework\DB\Select $select
387+
* @param Select $select
327388
* @param string $field
328389
* @param int $range
329-
* @return \Magento\Framework\DB\Select[]
390+
* @return Select[]
330391
*/
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+
{
336394
if ($this->isRangingNeeded()) {
337395
$iterator = $this->queryGenerator->generate(
338396
$field,
@@ -353,10 +411,10 @@ protected function prepareSelectsByRange(
353411
/**
354412
* Reindex products of non anchor categories
355413
*
356-
* @param \Magento\Store\Model\Store $store
414+
* @param Store $store
357415
* @return void
358416
*/
359-
protected function reindexNonAnchorCategories(\Magento\Store\Model\Store $store)
417+
protected function reindexNonAnchorCategories(Store $store)
360418
{
361419
$selects = $this->prepareSelectsByRange($this->getNonAnchorCategoriesSelect($store), 'entity_id');
362420
foreach ($selects as $select) {
@@ -374,43 +432,44 @@ protected function reindexNonAnchorCategories(\Magento\Store\Model\Store $store)
374432
/**
375433
* Check if anchor select isset
376434
*
377-
* @param \Magento\Store\Model\Store $store
435+
* @param Store $store
378436
* @return bool
379437
*/
380-
protected function hasAnchorSelect(\Magento\Store\Model\Store $store)
438+
protected function hasAnchorSelect(Store $store)
381439
{
382440
return isset($this->anchorSelects[$store->getId()]);
383441
}
384442

385443
/**
386444
* Create anchor select
387445
*
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
390449
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
391450
*/
392-
protected function createAnchorSelect(\Magento\Store\Model\Store $store)
451+
protected function createAnchorSelect(Store $store)
393452
{
394453
$isAnchorAttributeId = $this->config->getAttribute(
395454
\Magento\Catalog\Model\Category::ENTITY,
396455
'is_anchor'
397456
)->getId();
398-
$statusAttributeId = $this->config->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'status')->getId();
457+
$statusAttributeId = $this->config->getAttribute(Product::ENTITY, 'status')->getId();
399458
$visibilityAttributeId = $this->config->getAttribute(
400-
\Magento\Catalog\Model\Product::ENTITY,
459+
Product::ENTITY,
401460
'visibility'
402461
)->getId();
403462
$rootCatIds = explode('/', $this->getPathFromCategoryId($store->getRootCategoryId()));
404463
array_pop($rootCatIds);
405464

406465
$temporaryTreeTable = $this->makeTempCategoryTreeIndex();
407466

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);
410469
$productLinkField = $productMetadata->getLinkField();
411470
$categoryLinkField = $categoryMetadata->getLinkField();
412471

413-
return $this->connection->select()->from(
472+
$select = $this->connection->select()->from(
414473
['cc' => $this->getTable('catalog_category_entity')],
415474
[]
416475
)->joinInner(
@@ -492,6 +551,10 @@ protected function createAnchorSelect(\Magento\Store\Model\Store $store)
492551
'visibility' => new \Zend_Db_Expr($this->connection->getIfNullSql('cpvs.value', 'cpvd.value')),
493552
]
494553
);
554+
555+
$this->addFilteringByChildProductsToSelect($select, $store);
556+
557+
return $select;
495558
}
496559

497560
/**
@@ -586,10 +649,10 @@ protected function fillTempCategoryTreeIndex($temporaryName)
586649
/**
587650
* Retrieve select for reindex products of non anchor categories
588651
*
589-
* @param \Magento\Store\Model\Store $store
590-
* @return \Magento\Framework\DB\Select
652+
* @param Store $store
653+
* @return Select
591654
*/
592-
protected function getAnchorCategoriesSelect(\Magento\Store\Model\Store $store)
655+
protected function getAnchorCategoriesSelect(Store $store)
593656
{
594657
if (!$this->hasAnchorSelect($store)) {
595658
$this->anchorSelects[$store->getId()] = $this->createAnchorSelect($store);
@@ -600,10 +663,10 @@ protected function getAnchorCategoriesSelect(\Magento\Store\Model\Store $store)
600663
/**
601664
* Reindex products of anchor categories
602665
*
603-
* @param \Magento\Store\Model\Store $store
666+
* @param Store $store
604667
* @return void
605668
*/
606-
protected function reindexAnchorCategories(\Magento\Store\Model\Store $store)
669+
protected function reindexAnchorCategories(Store $store)
607670
{
608671
$selects = $this->prepareSelectsByRange($this->getAnchorCategoriesSelect($store), 'entity_id');
609672

@@ -622,22 +685,23 @@ protected function reindexAnchorCategories(\Magento\Store\Model\Store $store)
622685
/**
623686
* Get select for all products
624687
*
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
627691
*/
628-
protected function getAllProducts(\Magento\Store\Model\Store $store)
692+
protected function getAllProducts(Store $store)
629693
{
630694
if (!isset($this->productsSelects[$store->getId()])) {
631695
$statusAttributeId = $this->config->getAttribute(
632-
\Magento\Catalog\Model\Product::ENTITY,
696+
Product::ENTITY,
633697
'status'
634698
)->getId();
635699
$visibilityAttributeId = $this->config->getAttribute(
636-
\Magento\Catalog\Model\Product::ENTITY,
700+
Product::ENTITY,
637701
'visibility'
638702
)->getId();
639703

640-
$metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
704+
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
641705
$linkField = $metadata->getLinkField();
642706

643707
$select = $this->connection->select()->from(
@@ -726,10 +790,10 @@ protected function isIndexRootCategoryNeeded()
726790
/**
727791
* Reindex all products to root category
728792
*
729-
* @param \Magento\Store\Model\Store $store
793+
* @param Store $store
730794
* @return void
731795
*/
732-
protected function reindexRootCategory(\Magento\Store\Model\Store $store)
796+
protected function reindexRootCategory(Store $store)
733797
{
734798
if ($this->isIndexRootCategoryNeeded()) {
735799
$selects = $this->prepareSelectsByRange(
@@ -750,16 +814,4 @@ protected function reindexRootCategory(\Magento\Store\Model\Store $store)
750814
}
751815
}
752816
}
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-
}
765817
}

0 commit comments

Comments
 (0)