Skip to content

Commit b51f02a

Browse files
MC-25125: Layered Navigation with different Scope for attribute
1 parent 6159dc8 commit b51f02a

File tree

5 files changed

+234
-25
lines changed

5 files changed

+234
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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\Store\Api\WebsiteRepositoryInterface;
9+
use Magento\TestFramework\Helper\Bootstrap;
10+
11+
require __DIR__ . '/category_with_different_price_products.php';
12+
require __DIR__ . '/../../Store/_files/second_website_with_two_stores.php';
13+
14+
$objectManager = Bootstrap::getObjectManager();
15+
/** @var WebsiteRepositoryInterface $websiteRepository */
16+
$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class);
17+
$defaultWebsiteId = $websiteRepository->get('base')->getId();
18+
$websiteId = $websiteRepository->get('test')->getId();
19+
20+
$product = $productRepository->get('simple1000');
21+
$product->setWebsiteIds([$defaultWebsiteId, $websiteId]);
22+
$productRepository->save($product);
23+
24+
$product = $productRepository->get('simple1001');
25+
$product->setWebsiteIds([$defaultWebsiteId, $websiteId]);
26+
$productRepository->save($product);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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+
require __DIR__ . '/category_with_different_price_products_rollback.php';
9+
require __DIR__ . '/../../Store/_files/second_website_with_two_stores_rollback.php';

dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractFiltersTest.php

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,9 @@ protected function setUp()
7474
$this->objectManager = Bootstrap::getObjectManager();
7575
$this->categoryCollectionFactory = $this->objectManager->create(CollectionFactory::class);
7676
$this->layout = $this->objectManager->get(LayoutInterface::class);
77-
$layerResolver = $this->objectManager->create(Resolver::class);
78-
79-
if ($this->getLayerType() === Resolver::CATALOG_LAYER_SEARCH) {
80-
$layerResolver->create(Resolver::CATALOG_LAYER_SEARCH);
81-
$this->navigationBlock = $this->objectManager->create(
82-
SearchNavigationBlock::class,
83-
[
84-
'layerResolver' => $layerResolver,
85-
]
86-
);
87-
} else {
88-
$this->navigationBlock = $this->objectManager->create(CategoryNavigationBlock::class);
89-
}
90-
9177
$this->attributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class);
9278
$this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
79+
$this->createNavigationBlockInstance();
9380
}
9481

9582
/**
@@ -121,7 +108,7 @@ protected function getCategoryFiltersAndAssert(
121108
array $expectation,
122109
string $categoryName
123110
): void {
124-
$this->updateAttribute($this->getAttributeCode(), $attributeData);
111+
$this->updateAttribute($attributeData);
125112
$this->updateProducts($products, $this->getAttributeCode());
126113
$this->clearInstanceAndReindexSearch();
127114
$category = $this->loadCategory($categoryName, Store::DEFAULT_STORE_ID);
@@ -150,7 +137,7 @@ protected function getSearchFiltersAndAssert(
150137
array $attributeData,
151138
array $expectation
152139
): void {
153-
$this->updateAttribute($this->getAttributeCode(), $attributeData);
140+
$this->updateAttribute($attributeData);
154141
$this->updateProducts($products, $this->getAttributeCode());
155142
$this->clearInstanceAndReindexSearch();
156143
$this->navigationBlock->getRequest()->setParams(['q' => 'Simple Product']);
@@ -188,15 +175,13 @@ function (AbstractFilter $filter) use ($code) {
188175
/**
189176
* Updates attribute data.
190177
*
191-
* @param string $attributeCode
192178
* @param array $data
193179
* @return void
194180
*/
195181
protected function updateAttribute(
196-
string $attributeCode,
197182
array $data
198183
): void {
199-
$attribute = $this->attributeRepository->get($attributeCode);
184+
$attribute = $this->attributeRepository->get($this->getAttributeCode());
200185
$attribute->addData($data);
201186
$this->attributeRepository->save($attribute);
202187
}
@@ -226,14 +211,18 @@ protected function prepareFilterItems(AbstractFilter $filter): array
226211
*
227212
* @param array $products
228213
* @param string $attributeCode
214+
* @param int $storeId
229215
* @return void
230216
*/
231-
protected function updateProducts(array $products, string $attributeCode): void
232-
{
217+
protected function updateProducts(
218+
array $products,
219+
string $attributeCode,
220+
int $storeId = Store::DEFAULT_STORE_ID
221+
): void {
233222
$attribute = $this->attributeRepository->get($attributeCode);
234223

235224
foreach ($products as $productSku => $stringValue) {
236-
$product = $this->productRepository->get($productSku, false, Store::DEFAULT_STORE_ID, true);
225+
$product = $this->productRepository->get($productSku, false, $storeId, true);
237226
$product->addData(
238227
[$attribute->getAttributeCode() => $attribute->getSource()->getOptionId($stringValue)]
239228
);
@@ -275,4 +264,26 @@ protected function loadCategory(string $categoryName, int $storeId): CategoryInt
275264

276265
return $category;
277266
}
267+
268+
/**
269+
* Creates navigation block instance.
270+
*
271+
* @return void
272+
*/
273+
protected function createNavigationBlockInstance(): void
274+
{
275+
$layerResolver = $this->objectManager->create(Resolver::class);
276+
277+
if ($this->getLayerType() === Resolver::CATALOG_LAYER_SEARCH) {
278+
$layerResolver->create(Resolver::CATALOG_LAYER_SEARCH);
279+
$this->navigationBlock = $this->objectManager->create(
280+
SearchNavigationBlock::class,
281+
[
282+
'layerResolver' => $layerResolver,
283+
]
284+
);
285+
} else {
286+
$this->navigationBlock = $this->objectManager->create(CategoryNavigationBlock::class);
287+
}
288+
}
278289
}

dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,15 @@ protected function prepareFilterItems(AbstractFilter $filter): array
7474
/**
7575
* @inheritdoc
7676
*/
77-
protected function updateProducts(array $products, string $attributeCode): void
78-
{
77+
protected function updateProducts(
78+
array $products,
79+
string $attributeCode,
80+
int $storeId = Store::DEFAULT_STORE_ID
81+
): void {
7982
$attribute = $this->attributeRepository->get($attributeCode);
8083

8184
foreach ($products as $productSku => $value) {
82-
$product = $this->productRepository->get($productSku, false, Store::DEFAULT_STORE_ID, true);
85+
$product = $this->productRepository->get($productSku, false, $storeId, true);
8386
$product->addData(
8487
[$attribute->getAttributeCode() => $value]
8588
);
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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\LayeredNavigation\Block\Navigation\Category;
9+
10+
use Magento\Catalog\Model\Layer\Filter\AbstractFilter;
11+
use Magento\Catalog\Model\Layer\Resolver;
12+
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
13+
use Magento\LayeredNavigation\Block\Navigation\AbstractFiltersTest;
14+
use Magento\Store\Model\StoreManagerInterface;
15+
16+
/**
17+
* Provides tests for custom filter with different scopes in navigation block on category page.
18+
*
19+
* @magentoAppArea frontend
20+
* @magentoAppIsolation enabled
21+
* @magentoDbIsolation disabled
22+
*/
23+
class FilterScopeTest extends AbstractFiltersTest
24+
{
25+
/**
26+
* @var StoreManagerInterface
27+
*/
28+
private $storeManager;
29+
30+
/**
31+
* @var int
32+
*/
33+
private $oldStoreId;
34+
35+
/**
36+
* @var int
37+
*/
38+
private $currentStoreId;
39+
40+
/**
41+
* @inheritdoc
42+
*/
43+
protected function setUp()
44+
{
45+
parent::setUp();
46+
$this->storeManager = $this->objectManager->get(StoreManagerInterface::class);
47+
$this->oldStoreId = (int)$this->storeManager->getStore()->getId();
48+
$this->currentStoreId = (int)$this->storeManager->getStore('fixture_second_store')->getId();
49+
}
50+
51+
/**
52+
* @magentoDataFixture Magento/Catalog/_files/product_dropdown_attribute.php
53+
* @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products_on_two_websites.php
54+
* @dataProvider filtersWithScopeDataProvider
55+
* @param int $scope
56+
* @param array $products
57+
* @param array $expectation
58+
* @return void
59+
*/
60+
public function testGetFilters(int $scope, array $products, array $expectation): void
61+
{
62+
$this->updateAttribute(
63+
[
64+
'is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS,
65+
'is_global' => $scope,
66+
]
67+
);
68+
$this->updateProductsOnStore($products);
69+
$this->clearInstanceAndReindexSearch();
70+
try {
71+
$this->storeManager->setCurrentStore($this->currentStoreId);
72+
$this->navigationBlock->getLayer()->setCurrentCategory(
73+
$this->loadCategory('Category 999', $this->currentStoreId)
74+
);
75+
$this->navigationBlock->setLayout($this->layout);
76+
$filter = $this->getFilterByCode($this->navigationBlock->getFilters(), $this->getAttributeCode());
77+
$this->assertNotNull($filter);
78+
$this->assertEquals($expectation, $this->prepareFilterItems($filter));
79+
} finally {
80+
$this->storeManager->setCurrentStore($this->oldStoreId);
81+
}
82+
}
83+
84+
/**
85+
* @return array
86+
*/
87+
public function filtersWithScopeDataProvider(): array
88+
{
89+
return [
90+
'with_scope_store' => [
91+
'scope' => ScopedAttributeInterface::SCOPE_STORE,
92+
'products' => [
93+
'default' => ['simple1000' => 'Option 1', 'simple1001' => 'Option 2'],
94+
'fixture_second_store' => ['simple1000' => 'Option 2', 'simple1001' => 'Option 3'],
95+
],
96+
'expectation' => [
97+
['label' => 'Option 2', 'count' => 1],
98+
['label' => 'Option 3', 'count' => 1],
99+
],
100+
],
101+
'with_scope_website' => [
102+
'scope' => ScopedAttributeInterface::SCOPE_WEBSITE,
103+
'products' => [
104+
'default' => ['simple1000' => 'Option 3', 'simple1001' => 'Option 2'],
105+
'fixture_second_store' => ['simple1000' => 'Option 1', 'simple1001' => 'Option 2'],
106+
],
107+
'expectation' => [
108+
['label' => 'Option 1', 'count' => 1],
109+
['label' => 'Option 2', 'count' => 1],
110+
],
111+
],
112+
'with_scope_global' => [
113+
'scope' => ScopedAttributeInterface::SCOPE_GLOBAL,
114+
'products' => [
115+
'default' => ['simple1000' => 'Option 1'],
116+
'fixture_second_store' => ['simple1001' => 'Option 2'],
117+
],
118+
'expectation' => [
119+
['label' => 'Option 1', 'count' => 1],
120+
['label' => 'Option 2', 'count' => 1],
121+
],
122+
],
123+
];
124+
}
125+
126+
/**
127+
* @inheritdoc
128+
*/
129+
protected function getLayerType(): string
130+
{
131+
return Resolver::CATALOG_LAYER_CATEGORY;
132+
}
133+
134+
/**
135+
* @inheritdoc
136+
*/
137+
protected function getAttributeCode(): string
138+
{
139+
return 'dropdown_attribute';
140+
}
141+
142+
/**
143+
* Updates products data for store.
144+
*
145+
* @param array $productsData
146+
* @return void
147+
*/
148+
private function updateProductsOnStore(array $productsData): void
149+
{
150+
try {
151+
foreach ($productsData as $storeCode => $products) {
152+
$storeId = (int)$this->storeManager->getStore($storeCode)->getId();
153+
$this->storeManager->setCurrentStore($storeId);
154+
$this->updateProducts($products, $this->getAttributeCode(), $storeId);
155+
}
156+
} finally {
157+
$this->storeManager->setCurrentStore($this->oldStoreId);
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)