Skip to content

Commit 18a2755

Browse files
committed
Merge remote-tracking branch 'origin/MAGETWO-65245' into develop-pr2
2 parents ebec6d7 + 4a1d4fe commit 18a2755

File tree

6 files changed

+293
-6
lines changed

6 files changed

+293
-6
lines changed

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

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,20 @@
55
*/
66
namespace Magento\CatalogSearch\Model\Indexer;
77

8+
use Magento\Catalog\Api\Data\ProductInterface;
89
use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\FullFactory;
910
use Magento\CatalogSearch\Model\Indexer\Scope\State;
1011
use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource;
1112
use Magento\Framework\App\ObjectManager;
13+
use Magento\Framework\EntityManager\MetadataPool;
1214
use Magento\Framework\Search\Request\Config as SearchRequestConfig;
1315
use Magento\Framework\Search\Request\DimensionFactory;
1416
use Magento\Store\Model\StoreManagerInterface;
1517

1618
/**
17-
* Provide functionality for Fulltext Search indexing
19+
* Provide functionality for Fulltext Search indexing.
20+
*
21+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1822
*/
1923
class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface
2024
{
@@ -68,6 +72,13 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
6872
*/
6973
private $indexScopeState;
7074

75+
/**
76+
* Holder for MetadataPool instance.
77+
*
78+
* @var MetadataPool
79+
*/
80+
private $metadataPool = null;
81+
7182
/**
7283
* @param FullFactory $fullActionFactory
7384
* @param IndexerHandlerFactory $indexerHandlerFactory
@@ -122,11 +133,37 @@ public function execute($ids)
122133
]);
123134
foreach ($storeIds as $storeId) {
124135
$dimension = $this->dimensionFactory->create(['name' => 'scope', 'value' => $storeId]);
125-
$saveHandler->deleteIndex([$dimension], new \ArrayObject($ids));
136+
$productIds = array_unique(array_merge($ids, $this->getRelationsByChild($ids)));
137+
$saveHandler->deleteIndex([$dimension], new \ArrayObject($productIds));
126138
$saveHandler->saveIndex([$dimension], $this->fullAction->rebuildStoreIndex($storeId, $ids));
127139
}
128140
}
129141

142+
/**
143+
* Retrieve product relations by children.
144+
*
145+
* @param int|array $childIds
146+
* @return array
147+
*/
148+
private function getRelationsByChild($childIds)
149+
{
150+
$connection = $this->fulltextResource->getConnection();
151+
$linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField();
152+
$select = $connection->select()->from(
153+
['relation' => $this->fulltextResource->getTable('catalog_product_relation')],
154+
[]
155+
)->join(
156+
['cpe' => $this->fulltextResource->getTable('catalog_product_entity')],
157+
'cpe.' . $linkField . ' = relation.parent_id',
158+
['cpe.entity_id']
159+
)->where(
160+
'relation.child_id IN (?)',
161+
$childIds
162+
)->distinct(true);
163+
164+
return $connection->fetchCol($select);
165+
}
166+
130167
/**
131168
* Execute full indexation
132169
*
@@ -174,4 +211,18 @@ public function executeRow($id)
174211
{
175212
$this->execute([$id]);
176213
}
214+
215+
/**
216+
* Get Metadata Pool instance.
217+
*
218+
* @return MetadataPool
219+
*/
220+
private function getMetadataPool()
221+
{
222+
if ($this->metadataPool === null) {
223+
$this->metadataPool = ObjectManager::getInstance()->get(MetadataPool::class);
224+
}
225+
226+
return $this->metadataPool;
227+
}
177228
}

app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55
*/
66
namespace Magento\CatalogSearch\Test\Unit\Model\Indexer;
77

8-
use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource;
9-
use Magento\Framework\Search\Request\Config as SearchRequestConfig;
10-
use Magento\Framework\Search\Request\DimensionFactory;
8+
use Magento\Framework\DB\Adapter\Pdo\Mysql;
9+
use Magento\Framework\EntityManager\EntityMetadata;
1110
use Magento\Framework\Search\Request\Dimension;
11+
use Magento\Framework\Search\Request\DimensionFactory;
1212
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
13-
use \Magento\CatalogSearch\Model\Indexer\Fulltext\IndexSwitcher;
1413

1514
/**
1615
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -57,6 +56,13 @@ class FulltextTest extends \PHPUnit_Framework_TestCase
5756
*/
5857
private $indexSwitcher;
5958

59+
/**
60+
* Holder for MetadataPool mock instance.
61+
*
62+
* @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject
63+
*/
64+
private $metadataPool;
65+
6066
protected function setUp()
6167
{
6268
$this->fullAction = $this->getClassMock(\Magento\CatalogSearch\Model\Indexer\Fulltext\Action\Full::class);
@@ -98,6 +104,10 @@ protected function setUp()
98104
->setMethods(['switchIndex'])
99105
->getMock();
100106

107+
$this->metadataPool = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class)
108+
->disableOriginalConstructor()
109+
->getMock();
110+
101111
$objectManagerHelper = new ObjectManagerHelper($this);
102112
$this->model = $objectManagerHelper->getObject(
103113
\Magento\CatalogSearch\Model\Indexer\Fulltext::class,
@@ -112,6 +122,7 @@ protected function setUp()
112122
'indexSwitcher' => $this->indexSwitcher,
113123
]
114124
);
125+
$objectManagerHelper->setBackwardCompatibleProperty($this->model, 'metadataPool', $this->metadataPool);
115126
}
116127

117128
/**
@@ -134,6 +145,7 @@ public function testExecute()
134145
$this->fullAction->expects($this->exactly(2))
135146
->method('rebuildStoreIndex')
136147
->willReturn(new \ArrayObject([$indexData, $indexData]));
148+
$this->mockGetRelationsByChild($ids);
137149

138150
$this->model->execute($ids);
139151
}
@@ -190,6 +202,7 @@ public function testExecuteList()
190202
$this->fullAction->expects($this->exactly(2))
191203
->method('rebuildStoreIndex')
192204
->willReturn(new \ArrayObject([$indexData, $indexData]));
205+
$this->mockGetRelationsByChild($ids);
193206

194207
$this->model->executeList($ids);
195208
}
@@ -205,7 +218,84 @@ public function testExecuteRow()
205218
$this->fullAction->expects($this->exactly(2))
206219
->method('rebuildStoreIndex')
207220
->willReturn(new \ArrayObject([$indexData, $indexData]));
221+
$this->mockGetRelationsByChild([$id]);
208222

209223
$this->model->executeRow($id);
210224
}
225+
226+
/**
227+
* Mock getRelationsByChild() method.
228+
*
229+
* @param array $ids
230+
* @return void
231+
*/
232+
private function mockGetRelationsByChild(array $ids)
233+
{
234+
$testTable1 = 'testTable1';
235+
$testTable2 = 'testTable2';
236+
$fieldForParent = 'testLinkField';
237+
238+
$metadata = $this->getMockBuilder(EntityMetadata::class)
239+
->disableOriginalConstructor()
240+
->getMock();
241+
$metadata->expects($this->exactly(2))
242+
->method('getLinkField')
243+
->willReturn($fieldForParent);
244+
245+
$select = $this->getMockBuilder(\Magento\Framework\DB\Select::class)
246+
->disableOriginalConstructor()
247+
->getMock();
248+
$select->expects($this->exactly(2))
249+
->method('from')
250+
->with(['relation' => $testTable1])
251+
->willReturnSelf();
252+
$select->expects($this->exactly(2))
253+
->method('distinct')
254+
->with(true)
255+
->willReturnSelf();
256+
$select->expects($this->exactly(2))
257+
->method('where')
258+
->with('relation.child_id IN (?)', $ids)
259+
->willReturnSelf();
260+
$select->expects($this->exactly(2))
261+
->method('join')
262+
->with(
263+
['cpe' => $testTable2],
264+
'cpe.' . $fieldForParent . ' = relation.parent_id',
265+
['cpe.entity_id']
266+
)->willReturnSelf();
267+
268+
$connection = $this->getMockBuilder(Mysql::class)
269+
->disableOriginalConstructor()
270+
->getMock();
271+
$connection->expects($this->exactly(2))
272+
->method('select')
273+
->willReturn($select);
274+
$connection->expects($this->exactly(2))
275+
->method('fetchCol')
276+
->with($select)
277+
->willReturn($ids);
278+
279+
$this->fulltextResource->expects($this->exactly(2))
280+
->method('getConnection')
281+
->willReturn($connection);
282+
$this->fulltextResource->expects($this->exactly(4))
283+
->method('getTable')
284+
->withConsecutive(
285+
['catalog_product_relation'],
286+
['catalog_product_entity'],
287+
['catalog_product_relation'],
288+
['catalog_product_entity']
289+
)
290+
->will($this->onConsecutiveCalls(
291+
$testTable1,
292+
$testTable2,
293+
$testTable1,
294+
$testTable2
295+
));
296+
$this->metadataPool->expects($this->exactly(2))
297+
->method('getMetadata')
298+
->with(\Magento\Catalog\Api\Data\ProductInterface::class)
299+
->willReturn($metadata);
300+
}
211301
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\CatalogSearch\Test\Constraint;
8+
9+
use Magento\Catalog\Test\Page\Adminhtml\CatalogProductEdit;
10+
use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex;
11+
use Magento\CatalogSearch\Test\Fixture\CatalogSearchQuery;
12+
use Magento\CatalogSearch\Test\Page\AdvancedResult;
13+
use Magento\CatalogSearch\Test\Page\CatalogsearchResult;
14+
use Magento\Cms\Test\Page\CmsIndex;
15+
use Magento\ConfigurableProduct\Test\Fixture\ConfigurableProduct;
16+
use Magento\Mtf\Constraint\AbstractConstraint;
17+
use Magento\Mtf\Fixture\FixtureFactory;
18+
19+
/**
20+
* Assert search has no results after disabling configurable children.
21+
*/
22+
class AssertConfigurableWithDisabledOtpionCatalogSearchNoResult extends AbstractConstraint
23+
{
24+
/**
25+
* Assert search has no results and product list in absent after disabling configurable children.
26+
*
27+
* @param CatalogSearchQuery $catalogSearch
28+
* @param CatalogsearchResult $catalogsearchResult
29+
* @param FixtureFactory $fixtureFactory
30+
* @param CmsIndex $cmsIndex
31+
* @param CatalogProductIndex $productGrid
32+
* @param CatalogProductEdit $editProductPage
33+
* @param string|null $queryLength
34+
* @return void
35+
*
36+
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
37+
*/
38+
public function processAssert(
39+
CatalogSearchQuery $catalogSearch,
40+
AdvancedResult $resultPage,
41+
FixtureFactory $fixtureFactory,
42+
CmsIndex $cmsIndex,
43+
CatalogProductIndex $productGrid,
44+
CatalogProductEdit $editProductPage,
45+
$queryLength = null
46+
) {
47+
/** @var ConfigurableProduct $product */
48+
$product = $catalogSearch->getDataFieldConfig('query_text')['source']->getFirstProduct();
49+
50+
$matrix = isset($product->getConfigurableAttributesData()['matrix']) ?
51+
$product->getConfigurableAttributesData()['matrix'] :
52+
[];
53+
54+
foreach ($matrix as $option) {
55+
$product = $fixtureFactory->createByCode('catalogProductSimple', ['data' => ['status' => 'No']]);
56+
$filter = ['sku' => $option['sku']];
57+
$productGrid->open();
58+
$productGrid->getProductGrid()->searchAndOpen($filter);
59+
$editProductPage->getProductForm()->fill($product);
60+
$editProductPage->getFormPageActions()->save();
61+
}
62+
63+
$cmsIndex->open();
64+
$cmsIndex->getSearchBlock()->search($catalogSearch->getQueryText(), $queryLength);
65+
66+
do {
67+
$isProductVisible = $resultPage->getListProductBlock()->getProductItem($product)->isVisible();
68+
} while (!$isProductVisible && $resultPage->getBottomToolbar()->nextPage());
69+
70+
\PHPUnit_Framework_Assert::assertFalse(
71+
$isProductVisible,
72+
"A product with name '" . $product->getName() . "' was found."
73+
);
74+
}
75+
76+
/**
77+
* Returns a string representation of the object.
78+
*
79+
* @return string
80+
*/
81+
public function toString()
82+
{
83+
return 'Search result has not been found.';
84+
}
85+
}

dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,10 @@
9999
</data>
100100
<constraint name="Magento\CatalogSearch\Test\Constraint\AssertCatalogSearchResultOrder" />
101101
</variation>
102+
<variation name="SearchEntityResultsTestVariation18" summary="Search Configurable Product with Enabled and Disabled Children." ticketId="MAGETWO-69181">
103+
<data name="catalogSearch/data/query_text/value" xsi:type="string">configurableProduct::one_simple_product_not_visible_individually::name</data>
104+
<constraint name="Magento\CatalogSearch\Test\Constraint\AssertCatalogSearchResult" />
105+
<constraint name="Magento\CatalogSearch\Test\Constraint\AssertConfigurableWithDisabledOtpionCatalogSearchNoResult" />
106+
</variation>
102107
</testCase>
103108
</config>

dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,5 +1040,35 @@
10401040
<item name="dataset" xsi:type="string">default</item>
10411041
</field>
10421042
</dataset>
1043+
1044+
<dataset name="one_simple_product_not_visible_individually">
1045+
<field name="name" xsi:type="string">Test configurable product %isolation%</field>
1046+
<field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field>
1047+
<field name="price" xsi:type="array">
1048+
<item name="value" xsi:type="string">560</item>
1049+
</field>
1050+
<field name="product_has_weight" xsi:type="string">This item has weight</field>
1051+
<field name="weight" xsi:type="string">2</field>
1052+
<field name="status" xsi:type="string">Yes</field>
1053+
<field name="visibility" xsi:type="string">Catalog, Search</field>
1054+
<field name="tax_class_id" xsi:type="array">
1055+
<item name="dataset" xsi:type="string">taxable_goods</item>
1056+
</field>
1057+
<field name="url_key" xsi:type="string">configurable-product-%isolation%</field>
1058+
<field name="configurable_attributes_data" xsi:type="array">
1059+
<item name="dataset" xsi:type="string">one_option_with_simple_product_not_visible_individually</item>
1060+
</field>
1061+
<field name="quantity_and_stock_status" xsi:type="array">
1062+
<item name="is_in_stock" xsi:type="string">In Stock</item>
1063+
</field>
1064+
<field name="website_ids" xsi:type="array">
1065+
<item name="0" xsi:type="array">
1066+
<item name="dataset" xsi:type="string">default</item>
1067+
</item>
1068+
</field>
1069+
<field name="attribute_set_id" xsi:type="array">
1070+
<item name="dataset" xsi:type="string">default</item>
1071+
</field>
1072+
</dataset>
10431073
</repository>
10441074
</config>

0 commit comments

Comments
 (0)