Skip to content

Commit 6fc3cec

Browse files
committed
Merge remote-tracking branch 'origin/MC-30588' into 2.4-develop-pr23
2 parents 82f0afe + 5087eee commit 6fc3cec

File tree

3 files changed

+230
-37
lines changed

3 files changed

+230
-37
lines changed

app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Attribute.php

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@
66
namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin;
77

88
use Magento\CatalogSearch\Model\Indexer\Fulltext;
9+
use Magento\Framework\Model\AbstractModel;
10+
use Magento\Catalog\Model\ResourceModel\Attribute as AttributeResourceModel;
11+
use Magento\Framework\Search\Request\Config;
12+
use Magento\Framework\Indexer\IndexerRegistry;
13+
use Magento\Catalog\Api\Data\EavAttributeInterface;
914

1015
/**
1116
* Catalog search indexer plugin for catalog attribute.
1217
*/
1318
class Attribute extends AbstractPlugin
1419
{
1520
/**
16-
* @var \Magento\Framework\Search\Request\Config
21+
* @var Config
1722
*/
1823
private $config;
1924

@@ -33,12 +38,12 @@ class Attribute extends AbstractPlugin
3338
private $saveIsNew;
3439

3540
/**
36-
* @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry
37-
* @param \Magento\Framework\Search\Request\Config $config
41+
* @param IndexerRegistry $indexerRegistry
42+
* @param Config $config
3843
*/
3944
public function __construct(
40-
\Magento\Framework\Indexer\IndexerRegistry $indexerRegistry,
41-
\Magento\Framework\Search\Request\Config $config
45+
IndexerRegistry $indexerRegistry,
46+
Config $config
4247
) {
4348
parent::__construct($indexerRegistry);
4449
$this->config = $config;
@@ -47,36 +52,32 @@ public function __construct(
4752
/**
4853
* Check if indexer invalidation is needed on attribute save (searchable flag change)
4954
*
50-
* @param \Magento\Catalog\Model\ResourceModel\Attribute $subject
51-
* @param \Magento\Framework\Model\AbstractModel $attribute
55+
* @param AttributeResourceModel $subject
56+
* @param AbstractModel $attribute
5257
*
5358
* @return void
5459
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
5560
*/
5661
public function beforeSave(
57-
\Magento\Catalog\Model\ResourceModel\Attribute $subject,
58-
\Magento\Framework\Model\AbstractModel $attribute
62+
AttributeResourceModel $subject,
63+
AbstractModel $attribute
5964
) {
6065
$this->saveIsNew = $attribute->isObjectNew();
61-
$this->saveNeedInvalidation = (
62-
$attribute->dataHasChangedFor('is_searchable')
63-
|| $attribute->dataHasChangedFor('is_filterable')
64-
|| $attribute->dataHasChangedFor('is_visible_in_advanced_search')
65-
);
66+
$this->saveNeedInvalidation = $this->shouldInvalidateSearchIndex($attribute);
6667
}
6768

6869
/**
6970
* Invalidate indexer on attribute save (searchable flag change)
7071
*
71-
* @param \Magento\Catalog\Model\ResourceModel\Attribute $subject
72-
* @param \Magento\Catalog\Model\ResourceModel\Attribute $result
72+
* @param AttributeResourceModel $subject
73+
* @param AttributeResourceModel $result
7374
*
74-
* @return \Magento\Catalog\Model\ResourceModel\Attribute
75+
* @return AttributeResourceModel
7576
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
7677
*/
7778
public function afterSave(
78-
\Magento\Catalog\Model\ResourceModel\Attribute $subject,
79-
\Magento\Catalog\Model\ResourceModel\Attribute $result
79+
AttributeResourceModel $subject,
80+
AttributeResourceModel $result
8081
) {
8182
if ($this->saveNeedInvalidation) {
8283
$this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate();
@@ -91,35 +92,61 @@ public function afterSave(
9192
/**
9293
* Check if indexer invalidation is needed on searchable attribute delete
9394
*
94-
* @param \Magento\Catalog\Model\ResourceModel\Attribute $subject
95-
* @param \Magento\Framework\Model\AbstractModel $attribute
95+
* @param AttributeResourceModel $subject
96+
* @param AbstractModel $attribute
9697
*
9798
* @return void
9899
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
99100
*/
100101
public function beforeDelete(
101-
\Magento\Catalog\Model\ResourceModel\Attribute $subject,
102-
\Magento\Framework\Model\AbstractModel $attribute
102+
AttributeResourceModel $subject,
103+
AbstractModel $attribute
103104
) {
104105
$this->deleteNeedInvalidation = !$attribute->isObjectNew() && $attribute->getIsSearchable();
105106
}
106107

107108
/**
108109
* Invalidate indexer on searchable attribute delete
109110
*
110-
* @param \Magento\Catalog\Model\ResourceModel\Attribute $subject
111-
* @param \Magento\Catalog\Model\ResourceModel\Attribute $result
111+
* @param AttributeResourceModel $subject
112+
* @param AttributeResourceModel $result
112113
*
113-
* @return \Magento\Catalog\Model\ResourceModel\Attribute
114+
* @return AttributeResourceModel
114115
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
115116
*/
116117
public function afterDelete(
117-
\Magento\Catalog\Model\ResourceModel\Attribute $subject,
118-
\Magento\Catalog\Model\ResourceModel\Attribute $result
118+
AttributeResourceModel $subject,
119+
AttributeResourceModel $result
119120
) {
120121
if ($this->deleteNeedInvalidation) {
121122
$this->indexerRegistry->get(Fulltext::INDEXER_ID)->invalidate();
122123
}
123124
return $result;
124125
}
126+
127+
/**
128+
* Check if catalogsearch_fulltext index should be invalidated.
129+
*
130+
* @param AbstractModel $attribute
131+
* @return bool
132+
*/
133+
private function shouldInvalidateSearchIndex(
134+
AbstractModel $attribute
135+
):bool {
136+
$shouldInvalidate = false;
137+
$fields = [
138+
EavAttributeInterface::IS_SEARCHABLE,
139+
EavAttributeInterface::IS_FILTERABLE,
140+
EavAttributeInterface::IS_VISIBLE_IN_ADVANCED_SEARCH,
141+
];
142+
foreach ($fields as $field) {
143+
if ($this->saveIsNew && $attribute->getData($field)
144+
|| !$this->saveIsNew && $attribute->dataHasChangedFor($field)) {
145+
$shouldInvalidate = true;
146+
break;
147+
}
148+
}
149+
150+
return $shouldInvalidate;
151+
}
125152
}

app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Fulltext/Plugin/AttributeTest.php

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ protected function setUp()
6464
);
6565
$this->attributeMock = $this->createPartialMock(
6666
\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class,
67-
['dataHasChangedFor', 'isObjectNew', 'getIsSearchable']
67+
['dataHasChangedFor', 'isObjectNew', 'getIsSearchable', 'getData']
6868
);
6969
$this->config = $this->getMockBuilder(\Magento\Framework\Search\Request\Config::class)
7070
->disableOriginalConstructor()
@@ -85,7 +85,7 @@ public function testBeforeSave()
8585
->method('isObjectNew')
8686
->willReturn(true);
8787
$this->attributeMock->expects($this->once())
88-
->method('dataHasChangedFor')
88+
->method('getData')
8989
->with('is_searchable')
9090
->willReturn(true);
9191
$this->assertEquals(
@@ -102,29 +102,54 @@ public function testAfterSaveNoInvalidation()
102102
);
103103
}
104104

105-
public function testAfterSaveWithInvalidation()
105+
/**
106+
* Test afterSave with invalidation.
107+
*
108+
* @param bool $saveNeedInvalidation
109+
* @param bool $saveIsNew
110+
* @dataProvider afterSaveDataProvider
111+
*/
112+
public function testAfterSaveWithInvalidation(bool $saveNeedInvalidation, bool $saveIsNew)
106113
{
107114
$model = $this->objectManager->getObject(
108115
Attribute::class,
109116
[
110117
'indexerRegistry' => $this->indexerRegistryMock,
111118
'config' => $this->config,
112-
'saveNeedInvalidation' => true,
113-
'saveIsNew' => true
119+
'saveNeedInvalidation' => $saveNeedInvalidation,
120+
'saveIsNew' => $saveIsNew,
114121
]
115122
);
116123

117-
$this->indexerMock->expects($this->once())->method('invalidate');
118-
$this->prepareIndexer();
119-
$this->config->expects($this->once())
120-
->method('reset');
124+
if ($saveNeedInvalidation) {
125+
$this->indexerMock->expects($this->once())->method('invalidate');
126+
$this->prepareIndexer();
127+
}
128+
129+
if ($saveIsNew || $saveNeedInvalidation) {
130+
$this->config->expects($this->once())
131+
->method('reset');
132+
}
121133

122134
$this->assertEquals(
123135
$this->subjectMock,
124136
$model->afterSave($this->subjectMock, $this->subjectMock)
125137
);
126138
}
127139

140+
/**
141+
* @return array
142+
*/
143+
public function afterSaveDataProvider(): array
144+
{
145+
return [
146+
'save_new_with_invalidation' => ['saveNeedInvalidation' => true, 'isNew' => true],
147+
'save_new_without_invalidation' => ['saveNeedInvalidation' => false, 'isNew' => true],
148+
'update_existing_with_inalidation' => ['saveNeedInvalidation' => true, 'isNew' => false],
149+
'update_existing_without_inalidation' => ['saveNeedInvalidation' => false, 'isNew' => false],
150+
];
151+
}
152+
128153
public function testBeforeDelete()
129154
{
130155
$this->attributeMock->expects($this->once())
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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\CatalogSearch\Model\Indexer\Fulltext\Plugin;
9+
10+
use Magento\Catalog\Model\Product;
11+
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
12+
use Magento\TestFramework\Helper\Bootstrap;
13+
use PHPUnit\Framework\TestCase;
14+
use Magento\CatalogSearch\Model\Indexer\Fulltext\Processor;
15+
use Magento\Catalog\Api\Data\EavAttributeInterface;
16+
17+
/**
18+
* Check catalogsearch_fulltext index status after create product attribute.
19+
*/
20+
class AttributeTest extends TestCase
21+
{
22+
/**
23+
* @var Processor
24+
*/
25+
private $indexerProcessor;
26+
27+
/**
28+
* @var Attribute
29+
*/
30+
private $attribute;
31+
32+
/**
33+
* @inheritdoc
34+
*/
35+
protected function setUp()
36+
{
37+
$this->indexerProcessor = Bootstrap::getObjectManager()->create(Processor::class);
38+
$this->attribute = Bootstrap::getObjectManager()->create(Attribute::class);
39+
}
40+
41+
/**
42+
* Check index status on clean database.
43+
*
44+
* @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php
45+
*/
46+
public function testCheckIndexStatusOnCleanDatabase(): void
47+
{
48+
$this->assertTrue($this->indexerProcessor->getIndexer()->isValid());
49+
}
50+
51+
/**
52+
* Check index status after create non searchable attribute.
53+
*
54+
* @return void
55+
* @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php
56+
* @magentoDbIsolation enabled
57+
* @depends testCheckIndexStatusOnCleanDatabase
58+
*/
59+
public function testCheckIndexStatusAfterCreateNonSearchableAttribute(): void
60+
{
61+
$this->assertTrue($this->indexerProcessor->getIndexer()->isValid());
62+
}
63+
64+
/**
65+
* Check index status after non searchable attribute update.
66+
*
67+
* @return void
68+
* @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php
69+
* @magentoDbIsolation enabled
70+
* @depends testCheckIndexStatusOnCleanDatabase
71+
*/
72+
public function testCheckIndexStatusAfterNonSearchableAttributeUpdate(): void
73+
{
74+
$this->attribute->load('dropdown_attribute', 'attribute_code');
75+
$this->assertFalse($this->attribute->isObjectNew());
76+
$this->attribute->setIsGlobal(1)->save();
77+
$this->assertTrue($this->indexerProcessor->getIndexer()->isValid());
78+
}
79+
80+
/**
81+
* Check index status after create searchable attribute.
82+
*
83+
* @return void
84+
* @magentoDataFixture Magento/CatalogSearch/_files/search_attributes.php
85+
* @magentoDbIsolation enabled
86+
* @depends testCheckIndexStatusOnCleanDatabase
87+
*/
88+
public function testCheckIndexStatusAfterCreateSearchableAttribute(): void
89+
{
90+
$this->assertTrue($this->indexerProcessor->getIndexer()->isInvalid());
91+
}
92+
93+
/**
94+
* Check index status after update non searchable attribute to searchable.
95+
*
96+
* @param string $field
97+
* @return void
98+
* @dataProvider searchableAttributesDataProvider
99+
* @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php
100+
* @magentoDbIsolation enabled
101+
*/
102+
public function testCheckIndexStatusAfterUpdateNonSearchableAttributeToSearchable(string $field): void
103+
{
104+
$this->indexerProcessor->reindexAll();
105+
$this->assertTrue($this->indexerProcessor->getIndexer()->isValid());
106+
$this->attribute->loadByCode(Product::ENTITY, 'dropdown_attribute');
107+
$this->assertFalse($this->attribute->isObjectNew());
108+
$this->attribute->setData($field, true)->save();
109+
$this->assertFalse($this->indexerProcessor->getIndexer()->isValid());
110+
}
111+
112+
/**
113+
* @return array
114+
*/
115+
public function searchableAttributesDataProvider(): array
116+
{
117+
return [
118+
[EavAttributeInterface::IS_SEARCHABLE],
119+
[EavAttributeInterface::IS_FILTERABLE],
120+
[EavAttributeInterface::IS_VISIBLE_IN_ADVANCED_SEARCH]
121+
];
122+
}
123+
124+
/**
125+
* Check index status after update searchable attribute to non searchable.
126+
*
127+
* @return void
128+
* @magentoDataFixture Magento/CatalogSearch/_files/search_attributes.php
129+
* @magentoDbIsolation enabled
130+
* @depends testCheckIndexStatusOnCleanDatabase
131+
*/
132+
public function testCheckIndexStatusAfterUpdateSearchableAttributeToNonSearchable(): void
133+
{
134+
$this->indexerProcessor->reindexAll();
135+
$this->assertTrue($this->indexerProcessor->getIndexer()->isValid());
136+
$this->attribute->loadByCode(Product::ENTITY, 'test_catalog_view');
137+
$this->assertFalse($this->attribute->isObjectNew());
138+
$this->attribute->setIsFilterable(false)->save();
139+
$this->assertFalse($this->indexerProcessor->getIndexer()->isValid());
140+
}
141+
}

0 commit comments

Comments
 (0)