Skip to content

Commit cfa2a19

Browse files
committed
Merge branch 'MC-39569' into 2.4-bugfixes-121520
2 parents ad29452 + 513bb0b commit cfa2a19

File tree

6 files changed

+190
-36
lines changed

6 files changed

+190
-36
lines changed

app/code/Magento/Catalog/Observer/CategoryProductIndexer.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Magento\Catalog\Observer;
99

1010
use Magento\Catalog\Model\Indexer\Category\Product\Processor;
11+
use Magento\Catalog\Model\Indexer\Category\Flat\State as FlatState;
1112
use Magento\Framework\Event\Observer;
1213
use Magento\Framework\Event\ObserverInterface;
1314

@@ -21,12 +22,21 @@ class CategoryProductIndexer implements ObserverInterface
2122
*/
2223
private $processor;
2324

25+
/**
26+
* @var FlatState
27+
*/
28+
private $flatState;
29+
2430
/**
2531
* @param Processor $processor
32+
* @param FlatState $flatState
2633
*/
27-
public function __construct(Processor $processor)
28-
{
34+
public function __construct(
35+
Processor $processor,
36+
FlatState $flatState
37+
) {
2938
$this->processor = $processor;
39+
$this->flatState = $flatState;
3040
}
3141

3242
/**
@@ -35,7 +45,7 @@ public function __construct(Processor $processor)
3545
public function execute(Observer $observer): void
3646
{
3747
$productIds = $observer->getEvent()->getProductIds();
38-
if (!empty($productIds) && $this->processor->isIndexerScheduled()) {
48+
if (!empty($productIds) && $this->processor->isIndexerScheduled() && $this->flatState->isFlatEnabled()) {
3949
$this->processor->markIndexerAsInvalid();
4050
}
4151
}

dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -245,36 +245,6 @@ public function testCatalogCategoryProductIndexInvalidateAfterDelete(): void
245245
$this->assertEquals(StateInterface::STATUS_INVALID, $status);
246246
}
247247

248-
/**
249-
* Test invalidate reindex after change product position on category
250-
*
251-
* @magentoAppArea adminhtml
252-
* @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php
253-
*
254-
* @return void
255-
*/
256-
public function testCatalogCategoryProductIndexInvalidateAfterChangeProductPosition(): void
257-
{
258-
$this->indexer->setScheduled(true);
259-
$indexerShouldBeValid = $this->indexer->isValid();
260-
261-
$category = $this->getCategoryByName->execute('Category 999');
262-
263-
$category->setPostedProducts([
264-
$this->productResource->getIdBySku('simple1000') => 1,
265-
$this->productResource->getIdBySku('simple1001') => 2
266-
]);
267-
268-
$this->categoryResource->save($category);
269-
270-
$state = $this->indexer->getState();
271-
$state->loadByIndexer($this->indexer->getId());
272-
$status = $state->getStatus();
273-
274-
$this->assertTrue($indexerShouldBeValid);
275-
$this->assertEquals(StateInterface::STATUS_INVALID, $status);
276-
}
277-
278248
/**
279249
* @param int $count
280250
* @return Category[]

dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/CategoryTest.php

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,21 @@
1010

1111
use Magento\Catalog\Api\CategoryRepositoryInterface;
1212
use Magento\Catalog\Model\Category as CategoryModel;
13+
use Magento\Catalog\Model\Indexer\Category\Product as CategoryProductIndexer;
14+
use Magento\Catalog\Model\Indexer\Product\Category as ProductCategoryIndexer;
1315
use Magento\Catalog\Model\ResourceModel\Category as CategoryResource;
1416
use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
1517
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
18+
use Magento\Catalog\Model\ResourceModel\Product as ProductResource;
19+
use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection;
1620
use Magento\Framework\App\Filesystem\DirectoryList;
1721
use Magento\Framework\Filesystem;
1822
use Magento\Framework\Filesystem\Directory\WriteInterface;
23+
use Magento\Framework\Indexer\IndexerInterface;
24+
use Magento\Framework\Indexer\IndexerRegistry;
1925
use Magento\Framework\ObjectManagerInterface;
2026
use Magento\Framework\UrlInterface;
27+
use Magento\Indexer\Cron\UpdateMview;
2128
use Magento\Store\Model\StoreManagerInterface;
2229
use Magento\TestFramework\Helper\Bootstrap;
2330
use PHPUnit\Framework\TestCase;
@@ -26,6 +33,7 @@
2633
* Tests category resource model
2734
*
2835
* @see \Magento\Catalog\Model\ResourceModel\Category
36+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2937
*/
3038
class CategoryTest extends TestCase
3139
{
@@ -54,6 +62,11 @@ class CategoryTest extends TestCase
5462
/** @var WriteInterface */
5563
private $mediaDirectory;
5664

65+
/**
66+
* @var ProductResource
67+
*/
68+
private $productResource;
69+
5770
/**
5871
* @inheritdoc
5972
*/
@@ -68,6 +81,7 @@ protected function setUp(): void
6881
$this->categoryCollection = $this->objectManager->get(CategoryCollectionFactory::class)->create();
6982
$this->filesystem = $this->objectManager->get(Filesystem::class);
7083
$this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
84+
$this->productResource = Bootstrap::getObjectManager()->get(ProductResource::class);
7185
}
7286

7387
/**
@@ -116,6 +130,128 @@ public function testAddImageForCategory(): void
116130
$this->assertFileExists($this->mediaDirectory->getAbsolutePath($imageRelativePath));
117131
}
118132

133+
/**
134+
* Test that adding or removing products in a category should not trigger full reindex in scheduled update mode
135+
*
136+
* @magentoAppArea adminhtml
137+
* @magentoAppIsolation enabled
138+
* @magentoDbIsolation disabled
139+
* @magentoDataFixture Magento/Catalog/_files/category_with_three_products.php
140+
* @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
141+
* @magentoDataFixture Magento/Catalog/_files/catalog_category_product_reindex_all.php
142+
* @magentoDataFixture Magento/Catalog/_files/catalog_product_category_reindex_all.php
143+
* @magentoDataFixture Magento/Catalog/_files/enable_catalog_product_reindex_schedule.php
144+
* @dataProvider catalogProductChangesWithScheduledUpdateDataProvider
145+
* @param array $products
146+
* @return void
147+
*/
148+
public function testCatalogProductChangesWithScheduledUpdate(array $products): void
149+
{
150+
// products are ordered by entity_id DESC because their positions are same and equal to 0
151+
$initialProducts = ['simple1002', 'simple1001', 'simple1000'];
152+
$defaultStoreId = (int) $this->storeManager->getDefaultStoreView()->getId();
153+
$category = $this->getCategory(['name' => 'Category 999']);
154+
$expectedProducts = array_keys($products);
155+
$productIdsBySkus = $this->productResource->getProductsIdsBySkus($expectedProducts);
156+
$postedProducts = [];
157+
foreach ($products as $sku => $position) {
158+
$postedProducts[$productIdsBySkus[$sku]] = $position;
159+
}
160+
$category->setPostedProducts($postedProducts);
161+
$this->categoryResource->save($category);
162+
// Indices should not be invalidated when adding/removing/reordering products in a category.
163+
$categoryProductIndexer = $this->getIndexer(CategoryProductIndexer::INDEXER_ID);
164+
$this->assertTrue(
165+
$categoryProductIndexer->isValid(),
166+
'"Indexed category/products association" indexer should not be invalidated.'
167+
);
168+
$productCategoryIndexer = $this->getIndexer(ProductCategoryIndexer::INDEXER_ID);
169+
$this->assertTrue(
170+
$productCategoryIndexer->isValid(),
171+
'"Indexed product/categories association" indexer should not be invalidated.'
172+
);
173+
// catalog products is not update until partial reindex occurs
174+
$collection = $this->getCategoryProducts($category, $defaultStoreId);
175+
$this->assertEquals($initialProducts, $collection->getColumnValues('sku'));
176+
// Execute MVIEW cron handler for cron job "indexer_update_all_views"
177+
/** @var $mViewCron UpdateMview */
178+
$mViewCron = $this->objectManager->create(UpdateMview::class);
179+
$mViewCron->execute();
180+
$collection = $this->getCategoryProducts($category, $defaultStoreId);
181+
$this->assertEquals($expectedProducts, $collection->getColumnValues('sku'));
182+
}
183+
184+
/**
185+
* @return array
186+
*/
187+
public function catalogProductChangesWithScheduledUpdateDataProvider(): array
188+
{
189+
return [
190+
'change products position' => [
191+
[
192+
'simple1002' => 1,
193+
'simple1000' => 2,
194+
'simple1001' => 3,
195+
]
196+
],
197+
'Add new product' => [
198+
[
199+
'simple1002' => 1,
200+
'simple1000' => 2,
201+
'simple-1' => 3,
202+
'simple1001' => 4,
203+
]
204+
],
205+
'Delete product' => [
206+
[
207+
'simple1002' => 1,
208+
'simple1000' => 2,
209+
]
210+
]
211+
];
212+
}
213+
214+
/**
215+
* @param CategoryModel $category
216+
* @param int $defaultStoreId
217+
* @return ProductCollection
218+
*/
219+
private function getCategoryProducts(CategoryModel $category, int $defaultStoreId)
220+
{
221+
/** @var ProductCollection $collection */
222+
$collection = $this->objectManager->create(ProductCollection::class);
223+
$collection->setStoreId($defaultStoreId);
224+
$collection->addCategoryFilter($category);
225+
$collection->addAttributeToSort('position');
226+
return $collection;
227+
}
228+
229+
/**
230+
* @param array $filters
231+
* @return CategoryModel
232+
*/
233+
private function getCategory(array $filters): CategoryModel
234+
{
235+
/** @var CategoryCollection $categoryCollection */
236+
$categoryCollection = $this->objectManager->create(CategoryCollection::class);
237+
foreach ($filters as $field => $value) {
238+
$categoryCollection->addFieldToFilter($field, $value);
239+
}
240+
241+
return $categoryCollection->getFirstItem();
242+
}
243+
244+
/**
245+
* @param string $indexerId
246+
* @return IndexerInterface
247+
*/
248+
private function getIndexer(string $indexerId): IndexerInterface
249+
{
250+
/** @var IndexerRegistry $indexerRegistry */
251+
$indexerRegistry = $this->objectManager->get(IndexerRegistry::class);
252+
return $indexerRegistry->get($indexerId);
253+
}
254+
119255
/**
120256
* Prepare image url for image data
121257
*
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
use Magento\Catalog\Model\Indexer\Category\Product as CategoryProductIndexer;
9+
use Magento\Framework\Indexer\IndexerRegistry;
10+
use Magento\TestFramework\Helper\Bootstrap;
11+
12+
/** @var IndexerRegistry $indexRegistry */
13+
$indexRegistry = Bootstrap::getObjectManager()->get(IndexerRegistry::class);
14+
15+
$model = $indexRegistry->get(CategoryProductIndexer::INDEXER_ID);
16+
$model->reindexAll();
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
use Magento\Catalog\Model\Indexer\Product\Category as ProductCategoryIndexer;
9+
use Magento\Framework\Indexer\IndexerRegistry;
10+
use Magento\TestFramework\Helper\Bootstrap;
11+
12+
/** @var IndexerRegistry $indexRegistry */
13+
$indexRegistry = Bootstrap::getObjectManager()->get(IndexerRegistry::class);
14+
15+
$model = $indexRegistry->get(ProductCategoryIndexer::INDEXER_ID);
16+
$model->reindexAll();

dev/tests/integration/testsuite/Magento/Catalog/_files/enable_catalog_product_reindex_schedule_rollback.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@
55
*/
66
declare(strict_types=1);
77

8-
use Magento\Catalog\Model\Indexer\Product\Price\Processor;
8+
use Magento\Framework\Indexer\IndexerRegistry;
99
use Magento\TestFramework\Helper\Bootstrap;
1010

11-
$indexerProcessor = Bootstrap::getObjectManager()->get(Processor::class);
12-
$indexerProcessor->getIndexer()->setScheduled(false);
11+
/** @var IndexerRegistry $indexRegistry */
12+
$indexRegistry = Bootstrap::getObjectManager()->get(IndexerRegistry::class);
13+
14+
$model = $indexRegistry->get('catalog_category_product');
15+
$model->setScheduled(false);
16+
17+
$model = $indexRegistry->get('catalog_product_category');
18+
$model->setScheduled(false);

0 commit comments

Comments
 (0)