Skip to content

Commit eb75b11

Browse files
Merge branch '2.2-develop' of github.com:magento/magento2ce into 2.2-bugs
2 parents 30e065c + 2eaf779 commit eb75b11

File tree

6 files changed

+238
-10
lines changed

6 files changed

+238
-10
lines changed

app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use Magento\Framework\App\ObjectManager;
1212
use Magento\Framework\App\ResourceConnection;
13+
use Magento\Framework\EntityManager\MetadataPool;
1314

1415
/**
1516
* Abstract action reindex class
@@ -70,25 +71,33 @@ abstract class AbstractAction
7071
*/
7172
private $cacheCleaner;
7273

74+
/**
75+
* @var MetadataPool
76+
*/
77+
private $metadataPool;
78+
7379
/**
7480
* @param ResourceConnection $resource
7581
* @param \Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory $indexerFactory
7682
* @param \Magento\Catalog\Model\Product\Type $catalogProductType
7783
* @param \Magento\Framework\Indexer\CacheContext $cacheContext
7884
* @param \Magento\Framework\Event\ManagerInterface $eventManager
85+
* @param MetadataPool|null $metadataPool
7986
*/
8087
public function __construct(
8188
ResourceConnection $resource,
8289
\Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory $indexerFactory,
8390
\Magento\Catalog\Model\Product\Type $catalogProductType,
8491
\Magento\Framework\Indexer\CacheContext $cacheContext,
85-
\Magento\Framework\Event\ManagerInterface $eventManager
92+
\Magento\Framework\Event\ManagerInterface $eventManager,
93+
MetadataPool $metadataPool = null
8694
) {
8795
$this->_resource = $resource;
8896
$this->_indexerFactory = $indexerFactory;
8997
$this->_catalogProductType = $catalogProductType;
9098
$this->cacheContext = $cacheContext;
9199
$this->eventManager = $eventManager;
100+
$this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(MetadataPool::class);
92101
}
93102

94103
/**
@@ -154,10 +163,15 @@ protected function _getTable($entityName)
154163
public function getRelationsByChild($childIds)
155164
{
156165
$connection = $this->_getConnection();
157-
$select = $connection->select()
158-
->from($this->_getTable('catalog_product_relation'), 'parent_id')
159-
->where('child_id IN(?)', $childIds);
160-
166+
$linkField = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
167+
->getLinkField();
168+
$select = $connection->select()->from(
169+
['cpe' => $this->_getTable('catalog_product_entity')],
170+
'entity_id'
171+
)->join(
172+
['relation' => $this->_getTable('catalog_product_relation')],
173+
'relation.parent_id = cpe.' . $linkField
174+
)->where('child_id IN(?)', $childIds);
161175
return $connection->fetchCol($select);
162176
}
163177

@@ -230,7 +244,8 @@ protected function _reindexRows($productIds = [])
230244
if (!is_array($productIds)) {
231245
$productIds = [$productIds];
232246
}
233-
247+
$parentIds = $this->getRelationsByChild($productIds);
248+
$productIds = $parentIds ? array_unique(array_merge($parentIds, $productIds)) : $productIds;
234249
$this->getCacheCleaner()->clean($productIds, function () use ($productIds) {
235250
$this->doReindex($productIds);
236251
});
@@ -248,13 +263,10 @@ private function doReindex($productIds = [])
248263
{
249264
$connection = $this->_getConnection();
250265

251-
$parentIds = $this->getRelationsByChild($productIds);
252-
$processIds = $parentIds ? array_merge($parentIds, $productIds) : $productIds;
253-
254266
// retrieve product types by processIds
255267
$select = $connection->select()
256268
->from($this->_getTable('catalog_product_entity'), ['entity_id', 'type_id'])
257-
->where('entity_id IN(?)', $processIds);
269+
->where('entity_id IN(?)', $productIds);
258270
$pairs = $connection->fetchPairs($select);
259271

260272
$byType = [];
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\CatalogInventory\Model\Plugin;
7+
8+
use Magento\Catalog\Model\Product\Action as ProductAction;
9+
10+
/**
11+
* Plugin for Magento\Catalog\Model\Product\Action
12+
*/
13+
class ReindexUpdatedProducts
14+
{
15+
/**
16+
* @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor
17+
*/
18+
private $indexerProcessor;
19+
20+
/**
21+
* @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $indexerProcessor
22+
*/
23+
public function __construct(\Magento\CatalogInventory\Model\Indexer\Stock\Processor $indexerProcessor)
24+
{
25+
$this->indexerProcessor = $indexerProcessor;
26+
}
27+
28+
/**
29+
* Reindex on product attribute mass change
30+
*
31+
* @param ProductAction $subject
32+
* @param ProductAction $action
33+
* @param array $productIds
34+
* @return ProductAction
35+
*
36+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
37+
*/
38+
public function afterUpdateAttributes(
39+
ProductAction $subject,
40+
ProductAction $action,
41+
$productIds
42+
) {
43+
$this->indexerProcessor->reindexList(array_unique($productIds));
44+
return $action;
45+
}
46+
}

app/code/Magento/CatalogInventory/etc/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
<type name="Magento\Catalog\Model\Product">
7979
<plugin name="catalogInventoryAfterLoad" type="Magento\CatalogInventory\Model\Plugin\AfterProductLoad"/>
8080
</type>
81+
<type name="Magento\Catalog\Model\Product\Action">
82+
<plugin name="ReindexUpdatedProducts" type="Magento\CatalogInventory\Model\Plugin\ReindexUpdatedProducts"/>
83+
</type>
8184
<type name="Magento\CatalogInventory\Setup\UpgradeData">
8285
<arguments>
8386
<argument name="indexerProcessor" xsi:type="object">Magento\CatalogInventory\Model\Indexer\Stock\Processor</argument>

dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ActionTest.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,73 @@ public function testUpdateWebsites()
8484
}
8585
}
8686

87+
/**
88+
* @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
89+
* @magentoAppArea adminhtml
90+
* @param string $status
91+
* @param string $productsCount
92+
* @dataProvider updateAttributesDataProvider
93+
*/
94+
public function testUpdateAttributes($status, $productsCount)
95+
{
96+
/** @var \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry */
97+
$indexerRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
98+
->get(\Magento\Framework\Indexer\IndexerRegistry::class);
99+
$indexerRegistry->get(Fulltext::INDEXER_ID)->setScheduled(false);
100+
101+
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
102+
$productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
103+
104+
/** @var \Magento\Catalog\Model\Product $product */
105+
$product = $productRepository->get('configurable');
106+
$productAttributesOptions = $product->getExtensionAttributes()->getConfigurableProductLinks();
107+
$attrData = ['status' => $status];
108+
$configurableOptionsId = [];
109+
if (isset($productAttributesOptions)) {
110+
foreach ($productAttributesOptions as $configurableOption) {
111+
$configurableOptionsId[] = $configurableOption;
112+
}
113+
}
114+
$this->action->updateAttributes($configurableOptionsId, $attrData, $product->getStoreId());
115+
116+
$categoryFactory = $this->objectManager->create(\Magento\Catalog\Model\CategoryFactory::class);
117+
/** @var \Magento\Catalog\Block\Product\ListProduct $listProduct */
118+
$listProduct = $this->objectManager->create(\Magento\Catalog\Block\Product\ListProduct::class);
119+
$category = $categoryFactory->create()->load(2);
120+
$layer = $listProduct->getLayer();
121+
$layer->setCurrentCategory($category);
122+
$productCollection = $layer->getProductCollection();
123+
$productCollection->joinField(
124+
'qty',
125+
'cataloginventory_stock_status',
126+
'qty',
127+
'product_id=entity_id',
128+
'{{table}}.stock_id=1',
129+
'left'
130+
);
131+
132+
$this->assertEquals($productsCount, $productCollection->count());
133+
}
134+
135+
/**
136+
* DataProvider for testUpdateAttributes
137+
*
138+
* @return array
139+
*/
140+
public function updateAttributesDataProvider()
141+
{
142+
return [
143+
[
144+
'status' => 2,
145+
'expected_count' => 0
146+
],
147+
[
148+
'status' => 1,
149+
'expected_count' => 1
150+
],
151+
];
152+
}
153+
87154
public static function tearDownAfterClass()
88155
{
89156
/** @var \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry */
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
use Magento\Catalog\Api\ProductRepositoryInterface;
7+
use Magento\Catalog\Model\Product;
8+
use Magento\Catalog\Model\Product\Attribute\Source\Status;
9+
use Magento\Catalog\Model\Product\Type;
10+
use Magento\Catalog\Model\Product\Visibility;
11+
use Magento\GroupedProduct\Model\Product\Type\Grouped;
12+
use Magento\TestFramework\Helper\Bootstrap;
13+
14+
/** @var ProductRepositoryInterface $productRepository */
15+
$productRepository = Bootstrap::getObjectManager()
16+
->get(ProductRepositoryInterface::class);
17+
18+
$productLinkFactory = Bootstrap::getObjectManager()
19+
->get(\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory::class);
20+
$productIds = ['11', '22'];
21+
22+
foreach ($productIds as $productId) {
23+
/** @var $product Product */
24+
$product = Bootstrap::getObjectManager()->create(Product::class);
25+
$product->setTypeId(Type::TYPE_SIMPLE)
26+
->setId($productId)
27+
->setWebsiteIds([1])
28+
->setAttributeSetId(4)
29+
->setName('Simple ' . $productId)
30+
->setSku('simple_' . $productId)
31+
->setPrice(100)
32+
->setVisibility(Visibility::VISIBILITY_BOTH)
33+
->setStatus(Status::STATUS_ENABLED)
34+
->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
35+
36+
$linkedProducts[] = $productRepository->save($product);
37+
}
38+
39+
/** @var $product Product */
40+
$product = Bootstrap::getObjectManager()->create(Product::class);
41+
42+
$product->setTypeId(Grouped::TYPE_CODE)
43+
->setId(1)
44+
->setWebsiteIds([1])
45+
->setAttributeSetId(4)
46+
->setName('Grouped Product')
47+
->setSku('grouped')
48+
->setVisibility(Visibility::VISIBILITY_BOTH)
49+
->setStatus(Status::STATUS_ENABLED)
50+
->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]);
51+
52+
foreach ($linkedProducts as $linkedProduct) {
53+
/** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */
54+
$productLink = $productLinkFactory->create();
55+
$productLink->setSku($product->getSku())
56+
->setLinkType('associated')
57+
->setLinkedProductSku($linkedProduct->getSku())
58+
->setLinkedProductType($linkedProduct->getTypeId())
59+
->getExtensionAttributes()
60+
->setQty(1);
61+
$newLinks[] = $productLink;
62+
}
63+
64+
$product->setProductLinks($newLinks);
65+
66+
$productRepository->save($product);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
7+
8+
/** @var \Magento\Framework\Registry $registry */
9+
$registry = $objectManager->get(\Magento\Framework\Registry::class);
10+
11+
$registry->unregister('isSecureArea');
12+
$registry->register('isSecureArea', true);
13+
14+
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
15+
$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
16+
->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
17+
18+
$skuList = ['simple_11', 'simple_22', 'grouped'];
19+
foreach ($skuList as $sku) {
20+
try {
21+
$product = $productRepository->get($sku, false, null, true);
22+
23+
$stockStatus = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Status::class);
24+
$stockStatus->load($product->getEntityId(), 'product_id');
25+
$stockStatus->delete();
26+
27+
$productRepository->delete($product);
28+
} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
29+
//Product already removed
30+
}
31+
}
32+
33+
$registry->unregister('isSecureArea');
34+
$registry->register('isSecureArea', false);

0 commit comments

Comments
 (0)