Skip to content

Commit 82f0afe

Browse files
committed
Merge remote-tracking branch 'origin/MC-25010' into 2.4-develop-pr23
2 parents b0c8317 + 1cd4fc2 commit 82f0afe

File tree

5 files changed

+448
-0
lines changed

5 files changed

+448
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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+
namespace Magento\Elasticsearch\Model\Indexer\Fulltext\Plugin\Category\Product\Action;
9+
10+
use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer;
11+
use Magento\Framework\App\ResourceConnection;
12+
use Magento\Framework\Indexer\IndexerRegistry;
13+
use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer;
14+
use Magento\Catalog\Model\Indexer\Category\Product\Action\Rows as ActionRows;
15+
use Magento\Store\Model\StoreManagerInterface;
16+
use Magento\Framework\DB\Adapter\AdapterInterface;
17+
18+
/**
19+
* Catalog search indexer plugin for catalog category products assignment.
20+
*/
21+
class Rows
22+
{
23+
/**
24+
* @var IndexerRegistry
25+
*/
26+
private $indexerRegistry;
27+
28+
/**
29+
* @var StoreManagerInterface
30+
*/
31+
private $storeManager;
32+
33+
/**
34+
* @var AdapterInterface
35+
*/
36+
private $connection;
37+
38+
/**
39+
* @var TableMaintainer
40+
*/
41+
private $tableMaintainer;
42+
43+
/**
44+
* @param IndexerRegistry $indexerRegistry
45+
* @param StoreManagerInterface $storeManager
46+
* @param ResourceConnection $resource
47+
* @param TableMaintainer $tableMaintainer
48+
*/
49+
public function __construct(
50+
IndexerRegistry $indexerRegistry,
51+
StoreManagerInterface $storeManager,
52+
ResourceConnection $resource,
53+
TableMaintainer $tableMaintainer
54+
) {
55+
$this->indexerRegistry = $indexerRegistry;
56+
$this->storeManager = $storeManager;
57+
$this->connection = $resource->getConnection();
58+
$this->tableMaintainer = $tableMaintainer;
59+
}
60+
61+
/**
62+
* Reindex after catalog category product reindex.
63+
*
64+
* @param ActionRows $subject
65+
* @param ActionRows $result
66+
* @param array $entityIds
67+
* @param bool $useTempTable
68+
* @return ActionRows
69+
*
70+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
71+
*/
72+
public function afterExecute(
73+
ActionRows $subject,
74+
ActionRows $result,
75+
array $entityIds,
76+
bool $useTempTable = false
77+
): ActionRows {
78+
$indexer = $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID);
79+
if (!empty($entityIds) && $indexer->isScheduled()) {
80+
foreach ($this->storeManager->getStores() as $store) {
81+
$indexTable = $this->getIndexTable((int) $store->getId(), $useTempTable);
82+
$productIds = $this->getProductIdsFromIndex($indexTable, $entityIds);
83+
if (!empty($productIds)) {
84+
$indexer->reindexList($productIds);
85+
}
86+
}
87+
}
88+
89+
return $result;
90+
}
91+
92+
/**
93+
* Return index table name.
94+
*
95+
* @param int $storeId
96+
* @param bool $useTempTable
97+
*
98+
* @return string
99+
*/
100+
private function getIndexTable(int $storeId, bool $useTempTable)
101+
{
102+
return $useTempTable
103+
? $this->tableMaintainer->getMainReplicaTable($storeId)
104+
: $this->tableMaintainer->getMainTable($storeId);
105+
}
106+
107+
/**
108+
* Get all category products from index table.
109+
*
110+
* @param string $indexTable
111+
* @param array $categoryIds
112+
*
113+
* @return array
114+
*/
115+
private function getProductIdsFromIndex(string $indexTable, array $categoryIds): array
116+
{
117+
return $this->connection->fetchCol(
118+
$this->connection->select()
119+
->from($indexTable, ['product_id'])
120+
->where('category_id IN (?)', $categoryIds)
121+
->distinct()
122+
);
123+
}
124+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
<preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\Converter" />
1414
<preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\Converter" />
1515
<preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\CompositeFieldProvider" />
16+
<type name="Magento\Catalog\Model\Indexer\Category\Product\Action\Rows">
17+
<plugin name="catalogsearchFulltextProductAssignment" type="Magento\Elasticsearch\Model\Indexer\Fulltext\Plugin\Category\Product\Action\Rows"/>
18+
</type>
1619
<type name="Magento\Elasticsearch\Model\Config">
1720
<arguments>
1821
<argument name="engineList" xsi:type="array">
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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\Api\CategoryRepositoryInterface;
9+
use Magento\Catalog\Api\Data\ProductInterfaceFactory;
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Catalog\Api\Data\CategoryInterfaceFactory;
12+
use Magento\Catalog\Model\Product\Attribute\Source\Status;
13+
use Magento\Catalog\Model\Product\Type;
14+
use Magento\Catalog\Model\Product\Visibility;
15+
use Magento\Store\Model\Store;
16+
use Magento\TestFramework\Helper\Bootstrap;
17+
use Magento\Eav\Model\Config;
18+
19+
$objectManager = Bootstrap::getObjectManager();
20+
$categoryFactory = $objectManager->get(CategoryInterfaceFactory::class);
21+
$productFactory = $objectManager->get(ProductInterfaceFactory::class);
22+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
23+
$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class);
24+
25+
$categoryA = $categoryFactory->create(
26+
[
27+
'data' => [
28+
'name' => 'Category A',
29+
'parent_id' => 2,
30+
'level' => 2,
31+
'position' => 1,
32+
'is_active' => true,
33+
'available_sort_by' =>['position', 'name'],
34+
'default_sort_by' => 'name',
35+
],
36+
]
37+
);
38+
$categoryA->isObjectNew(true);
39+
$categoryA = $categoryRepository->save($categoryA);
40+
41+
$categoryB = $categoryFactory->create(
42+
[
43+
'data' => [
44+
'name' => 'Category B',
45+
'parent_id' => 2,
46+
'level' => 2,
47+
'position' => 1,
48+
'is_active' => true,
49+
'available_sort_by' =>['position', 'name'],
50+
'default_sort_by' => 'name',
51+
],
52+
]
53+
);
54+
$categoryB->isObjectNew(true);
55+
$categoryB = $categoryRepository->save($categoryB);
56+
57+
$categoryC = $categoryFactory->create(
58+
[
59+
'data' => [
60+
'name' => 'Category C',
61+
'parent_id' => $categoryB->getId(),
62+
'level' => 2,
63+
'position' => 1,
64+
'is_active' => true,
65+
'available_sort_by' =>['position', 'name'],
66+
'default_sort_by' => 'name',
67+
],
68+
]
69+
);
70+
$categoryC->isObjectNew(true);
71+
$categoryC = $categoryRepository->save($categoryC);
72+
73+
$defaultAttributeSet = $objectManager->get(Config::class)
74+
->getEntityType('catalog_product')
75+
->getDefaultAttributeSetId();
76+
$product = $productFactory->create(
77+
[
78+
'data' => [
79+
'type_id' => Type::TYPE_SIMPLE,
80+
'attribute_set_id' => $defaultAttributeSet,
81+
'store_id' => Store::DEFAULT_STORE_ID,
82+
'website_ids' => [1],
83+
'name' => 'Simple Product B',
84+
'sku' => 'simpleB',
85+
'price' => 10,
86+
'weight' => 1,
87+
'stock_data' => ['use_config_manage_stock' => 0],
88+
'category_ids' => [$categoryB->getId()],
89+
'visibility' => Visibility::VISIBILITY_BOTH,
90+
'status' => Status::STATUS_ENABLED,
91+
],
92+
]
93+
);
94+
$productRepository->save($product);
95+
96+
$product = $productFactory->create(
97+
[
98+
'data' => [
99+
'type_id' => Type::TYPE_SIMPLE,
100+
'attribute_set_id' => $defaultAttributeSet,
101+
'store_id' => Store::DEFAULT_STORE_ID,
102+
'website_ids' => [1],
103+
'name' => 'Simple Product C',
104+
'sku' => 'simpleC',
105+
'price' => 20,
106+
'weight' => 1,
107+
'stock_data' => ['use_config_manage_stock' => 0],
108+
'category_ids' => [$categoryC->getId()],
109+
'visibility' => Visibility::VISIBILITY_BOTH,
110+
'status' => Status::STATUS_ENABLED,
111+
],
112+
]
113+
);
114+
$productRepository->save($product);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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\Api\CategoryRepositoryInterface;
9+
use Magento\Catalog\Api\ProductRepositoryInterface;
10+
use Magento\Framework\Exception\NoSuchEntityException;
11+
use Magento\Framework\Registry;
12+
use Magento\TestFramework\Helper\Bootstrap;
13+
14+
$objectManager = Bootstrap::getObjectManager();
15+
$registry = $objectManager->get(Registry::class);
16+
$registry->unregister('isSecureArea');
17+
$registry->register('isSecureArea', true);
18+
19+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
20+
$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class);
21+
22+
$productSkus = ['simpleB', 'simpleC'];
23+
24+
foreach ($productSkus as $productSku) {
25+
try {
26+
$productRepository->deleteById($productSku);
27+
} catch (NoSuchEntityException $e) {
28+
//Already deleted.
29+
}
30+
}
31+
32+
$categoriesNames = ['Category A', 'Category B', 'Category C'];
33+
34+
foreach ($categoriesNames as $categoryName) {
35+
try {
36+
$category = $categoryRepository->get($categoryName);
37+
$categoryRepository->delete($category);
38+
} catch (NoSuchEntityException $e) {
39+
//Already deleted.
40+
}
41+
}
42+
43+
$registry->unregister('isSecureArea');
44+
$registry->register('isSecureArea', false);

0 commit comments

Comments
 (0)