Skip to content

Commit 16a5930

Browse files
MC-30722: Layered Navigation with default product attribute "Price"
1 parent d71050a commit 16a5930

File tree

6 files changed

+293
-20
lines changed

6 files changed

+293
-20
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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\CategoryLinkManagementInterface;
9+
use Magento\TestFramework\Helper\Bootstrap;
10+
11+
require __DIR__ . '/category_with_different_price_products.php';
12+
require __DIR__ . '/second_product_simple.php';
13+
14+
$objectManager = Bootstrap::getObjectManager();
15+
/** @var CategoryLinkManagementInterface $categoryLinkManagement */
16+
$categoryLinkManagement = $objectManager->get(CategoryLinkManagementInterface::class);
17+
$categoryLinkManagement->assignProductToCategories('simple2', [$category->getId()]);
Lines changed: 9 additions & 0 deletions
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__ . '/second_product_simple_rollback.php';
9+
require __DIR__ . '/category_with_different_price_products_rollback.php';

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,11 @@ protected function updateProducts(
227227

228228
foreach ($products as $productSku => $stringValue) {
229229
$product = $this->productRepository->get($productSku, false, $storeId, true);
230+
$productValue = $attribute->usesSource()
231+
? $attribute->getSource()->getOptionId($stringValue)
232+
: $stringValue;
230233
$product->addData(
231-
[$attribute->getAttributeCode() => $attribute->getSource()->getOptionId($stringValue)]
234+
[$attribute->getAttributeCode() => $productValue]
232235
);
233236
$this->productRepository->save($product);
234237
}

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

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,25 +71,6 @@ protected function prepareFilterItems(AbstractFilter $filter): array
7171
return $items;
7272
}
7373

74-
/**
75-
* @inheritdoc
76-
*/
77-
protected function updateProducts(
78-
array $products,
79-
string $attributeCode,
80-
int $storeId = Store::DEFAULT_STORE_ID
81-
): void {
82-
$attribute = $this->attributeRepository->get($attributeCode);
83-
84-
foreach ($products as $productSku => $value) {
85-
$product = $this->productRepository->get($productSku, false, $storeId, true);
86-
$product->addData(
87-
[$attribute->getAttributeCode() => $value]
88-
);
89-
$this->productRepository->save($product);
90-
}
91-
}
92-
9374
/**
9475
* @return array
9576
*/
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
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\Resolver;
11+
use Magento\Framework\App\Config\MutableScopeConfigInterface;
12+
use Magento\Framework\App\ScopeInterface;
13+
use Magento\LayeredNavigation\Block\Navigation\AbstractFiltersTest;
14+
use Magento\Catalog\Model\Layer\Filter\AbstractFilter;
15+
use Magento\Catalog\Model\Layer\Filter\Item;
16+
use Magento\Store\Model\ScopeInterface as StoreScope;
17+
use Magento\Store\Model\Store;
18+
19+
/**
20+
* Provides price filter tests with different price ranges calculation in navigation block on category page.
21+
*
22+
* @magentoAppArea frontend
23+
* @magentoAppIsolation enabled
24+
* @magentoDbIsolation disabled
25+
*/
26+
class PriceFilterTest extends AbstractFiltersTest
27+
{
28+
/**
29+
* @var MutableScopeConfigInterface
30+
*/
31+
private $scopeConfig;
32+
33+
/**
34+
* @inheritdoc
35+
*/
36+
protected function setUp()
37+
{
38+
parent::setUp();
39+
$this->scopeConfig = $this->objectManager->get(MutableScopeConfigInterface::class);
40+
}
41+
42+
/**
43+
* @magentoDataFixture Magento/Catalog/_files/category_with_three_products.php
44+
* @dataProvider getFiltersDataProvider
45+
* @param array $config
46+
* @param array $products
47+
* @param array $expectation
48+
* @return void
49+
*/
50+
public function testGetFilters(array $config, array $products, array $expectation): void
51+
{
52+
$this->applyCatalogConfig($config);
53+
$this->getCategoryFiltersAndAssert(
54+
$products,
55+
['is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS],
56+
$expectation,
57+
'Category 999'
58+
);
59+
}
60+
61+
/**
62+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
63+
* @return array
64+
*/
65+
public function getFiltersDataProvider(): array
66+
{
67+
return [
68+
'auto_calculation_variation_with_small_price_difference' => [
69+
'config' => ['catalog/layered_navigation/price_range_calculation' => 'auto'],
70+
'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple2' => 50.00],
71+
'expectation' => [
72+
['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1],
73+
['label' => '$20.00 - $29.99', 'value' => '20-30', 'count' => 1],
74+
['label' => '$50.00 and above', 'value' => '50-', 'count' => 1],
75+
],
76+
],
77+
'auto_calculation_variation_with_big_price_difference' => [
78+
'config' => ['catalog/layered_navigation/price_range_calculation' => 'auto'],
79+
'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple2' => 300.00],
80+
'expectation' => [
81+
['label' => '$0.00 - $99.99', 'value' => '-100', 'count' => 2],
82+
['label' => '$300.00 and above', 'value' => '300-', 'count' => 1],
83+
],
84+
],
85+
'auto_calculation_variation_with_fixed_price_step' => [
86+
'config' => ['catalog/layered_navigation/price_range_calculation' => 'auto'],
87+
'products_data' => ['simple1000' => 300.00, 'simple1001' => 400.00, 'simple2' => 500.00],
88+
'expectation' => [
89+
['label' => '$300.00 - $399.99', 'value' => '300-400', 'count' => 1],
90+
['label' => '$400.00 - $499.99', 'value' => '400-500', 'count' => 1],
91+
['label' => '$500.00 and above', 'value' => '500-', 'count' => 1],
92+
],
93+
],
94+
'improved_calculation_variation_with_small_price_difference' => [
95+
'config' => [
96+
'catalog/layered_navigation/price_range_calculation' => 'improved',
97+
'catalog/layered_navigation/interval_division_limit' => 3,
98+
],
99+
'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple2' => 50.00],
100+
'expectation' => [
101+
['label' => '$0.00 - $49.99', 'value' => '-50', 'count' => 2],
102+
['label' => '$50.00 and above', 'value' => '50-', 'count' => 1],
103+
],
104+
],
105+
'improved_calculation_variation_with_big_price_difference' => [
106+
'config' => [
107+
'catalog/layered_navigation/price_range_calculation' => 'improved',
108+
'catalog/layered_navigation/interval_division_limit' => 3,
109+
],
110+
'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple2' => 300.00],
111+
'expectation' => [
112+
['label' => '$0.00 - $299.99', 'value' => '-300', 'count' => 2.0],
113+
['label' => '$300.00 and above', 'value' => '300-', 'count' => 1.0],
114+
],
115+
],
116+
'manual_calculation_with_price_step_200' => [
117+
'config' => [
118+
'catalog/layered_navigation/price_range_calculation' => 'manual',
119+
'catalog/layered_navigation/price_range_step' => 200,
120+
],
121+
'products_data' => ['simple1000' => 300.00, 'simple1001' => 300.00, 'simple2' => 500.00],
122+
'expectation' => [
123+
['label' => '$200.00 - $399.99', 'value' => '200-400', 'count' => 2],
124+
['label' => '$400.00 and above', 'value' => '400-', 'count' => 1],
125+
],
126+
],
127+
'manual_calculation_with_price_step_10' => [
128+
'config' => [
129+
'catalog/layered_navigation/price_range_calculation' => 'manual',
130+
'catalog/layered_navigation/price_range_step' => 10,
131+
],
132+
'products_data' => ['simple1000' => 300.00, 'simple1001' => 300.00, 'simple2' => 500.00],
133+
'expectation' => [
134+
['label' => '$300.00 - $309.99', 'value' => '300-310', 'count' => 2],
135+
['label' => '$500.00 and above', 'value' => '500-', 'count' => 1],
136+
],
137+
],
138+
'manual_calculation_with_number_of_intervals_10' => [
139+
'config' => [
140+
'catalog/layered_navigation/price_range_calculation' => 'manual',
141+
'catalog/layered_navigation/price_range_step' => 10,
142+
'catalog/layered_navigation/price_range_max_intervals' => 10,
143+
],
144+
'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple2' => 30.00],
145+
'expectation' => [
146+
['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1],
147+
['label' => '$20.00 - $29.99', 'value' => '20-30', 'count' => 1],
148+
['label' => '$30.00 and above', 'value' => '30-', 'count' => 1],
149+
],
150+
],
151+
'manual_calculation_with_number_of_intervals_2' => [
152+
'config' => [
153+
'catalog/layered_navigation/price_range_calculation' => 'manual',
154+
'catalog/layered_navigation/price_range_step' => 10,
155+
'catalog/layered_navigation/price_range_max_intervals' => 2,
156+
],
157+
'products_data' => ['simple1000' => 10.00, 'simple1001' => 20.00, 'simple2' => 30.00],
158+
'expectation' => [
159+
['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1],
160+
['label' => '$20.00 and above', 'value' => '20-', 'count' => 2],
161+
],
162+
],
163+
];
164+
}
165+
166+
/**
167+
* @inheritdoc
168+
*/
169+
protected function getLayerType(): string
170+
{
171+
return Resolver::CATALOG_LAYER_CATEGORY;
172+
}
173+
174+
/**
175+
* @inheritdoc
176+
*/
177+
protected function getAttributeCode(): string
178+
{
179+
return 'price';
180+
}
181+
182+
/**
183+
* @inheritdoc
184+
*/
185+
protected function prepareFilterItems(AbstractFilter $filter): array
186+
{
187+
$items = [];
188+
/** @var Item $item */
189+
foreach ($filter->getItems() as $item) {
190+
$items[] = [
191+
'label' => strip_tags(__($item->getData('label'))->render()),
192+
'value' => $item->getData('value'),
193+
'count' => $item->getData('count'),
194+
];
195+
}
196+
197+
return $items;
198+
}
199+
200+
/**
201+
* Updates price filter store configuration.
202+
*
203+
* @param array $config
204+
* @return void
205+
*/
206+
protected function applyCatalogConfig(array $config): void
207+
{
208+
foreach ($config as $path => $value) {
209+
$this->scopeConfig->setValue($path, $value, StoreScope::SCOPE_STORE, ScopeInterface::SCOPE_DEFAULT);
210+
}
211+
}
212+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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\Search;
9+
10+
use Magento\Catalog\Model\Layer\Filter\AbstractFilter;
11+
use Magento\Catalog\Model\Layer\Resolver;
12+
use Magento\LayeredNavigation\Block\Navigation\Category\PriceFilterTest as CategoryPriceFilterTest;
13+
14+
/**
15+
* Provides price filter tests with different price ranges calculation in navigation block on search page.
16+
*
17+
* @magentoAppArea frontend
18+
* @magentoAppIsolation enabled
19+
* @magentoDbIsolation disabled
20+
*/
21+
class PriceFilterTest extends CategoryPriceFilterTest
22+
{
23+
/**
24+
* @magentoDataFixture Magento/Catalog/_files/category_with_three_products.php
25+
* @dataProvider getFiltersDataProvider
26+
* @param array $config
27+
* @param array $products
28+
* @param array $expectation
29+
* @return void
30+
*/
31+
public function testGetFilters(array $config, array $products, array $expectation): void
32+
{
33+
$this->applyCatalogConfig($config);
34+
$this->getSearchFiltersAndAssert(
35+
$products,
36+
[
37+
'is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS,
38+
'is_filterable_in_search' => 1,
39+
],
40+
$expectation
41+
);
42+
}
43+
44+
/**
45+
* @inheritdoc
46+
*/
47+
protected function getLayerType(): string
48+
{
49+
return Resolver::CATALOG_LAYER_SEARCH;
50+
}
51+
}

0 commit comments

Comments
 (0)