Skip to content

Commit ac6941c

Browse files
committed
Merge remote-tracking branch 'local/ACP2E-2201' into PR_06_SEP_2023
2 parents 7e8f730 + 412997e commit ac6941c

File tree

4 files changed

+97
-50
lines changed

4 files changed

+97
-50
lines changed

app/code/Magento/Catalog/Test/Fixture/Attribute.php

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
namespace Magento\Catalog\Test\Fixture;
99

10-
use Magento\Catalog\Api\Data\ProductAttributeInterface;
1110
use Magento\Catalog\Api\ProductAttributeManagementInterface;
1211
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
1312
use Magento\Catalog\Model\Product;
@@ -96,6 +95,11 @@ class Attribute implements RevertibleDataFixtureInterface
9695
*/
9796
private ResourceModelAttribute $resourceModelAttribute;
9897

98+
/**
99+
* @var ProductAttributeRepositoryInterface
100+
*/
101+
private ProductAttributeRepositoryInterface $productAttributeRepository;
102+
99103
/**
100104
* @param ServiceFactory $serviceFactory
101105
* @param ProcessorInterface $dataProcessor
@@ -104,6 +108,7 @@ class Attribute implements RevertibleDataFixtureInterface
104108
* @param AttributeFactory $attributeFactory
105109
* @param DataMerger $dataMerger
106110
* @param ResourceModelAttribute $resourceModelAttribute
111+
* @param ProductAttributeRepositoryInterface $productAttributeRepository
107112
*/
108113
public function __construct(
109114
ServiceFactory $serviceFactory,
@@ -112,7 +117,8 @@ public function __construct(
112117
ProductAttributeManagementInterface $productAttributeManagement,
113118
AttributeFactory $attributeFactory,
114119
DataMerger $dataMerger,
115-
ResourceModelAttribute $resourceModelAttribute
120+
ResourceModelAttribute $resourceModelAttribute,
121+
ProductAttributeRepositoryInterface $productAttributeRepository
116122
) {
117123
$this->serviceFactory = $serviceFactory;
118124
$this->dataProcessor = $dataProcessor;
@@ -121,6 +127,7 @@ public function __construct(
121127
$this->attributeFactory = $attributeFactory;
122128
$this->dataMerger = $dataMerger;
123129
$this->resourceModelAttribute = $resourceModelAttribute;
130+
$this->productAttributeRepository = $productAttributeRepository;
124131
}
125132

126133
/**
@@ -134,16 +141,11 @@ public function apply(array $data = []): ?DataObject
134141
return $this->applyAttributeWithAdditionalData($data);
135142
}
136143

137-
$service = $this->serviceFactory->create(ProductAttributeRepositoryInterface::class, 'save');
138-
139-
/**
140-
* @var ProductAttributeInterface $attribute
141-
*/
142-
$attribute = $service->execute(
143-
[
144-
'attribute' => $this->prepareData(array_diff_key($data, self::DEFAULT_ATTRIBUTE_SET_DATA))
145-
]
144+
$attribute = $this->attributeFactory->createAttribute(
145+
EavAttribute::class,
146+
$this->prepareData(array_diff_key($data, self::DEFAULT_ATTRIBUTE_SET_DATA))
146147
);
148+
$attribute = $this->productAttributeRepository->save($attribute);
147149

148150
$attributeSetData = $this->prepareAttributeSetData(
149151
array_intersect_key($data, self::DEFAULT_ATTRIBUTE_SET_DATA)
@@ -201,6 +203,7 @@ private function applyAttributeWithAdditionalData(array $data = []): ?DataObject
201203
private function prepareData(array $data): array
202204
{
203205
$data = array_merge(self::DEFAULT_DATA, $data);
206+
$data['frontend_label'] ??= $data['default_frontend_label'];
204207

205208
return $this->dataProcessor->process($this, $data);
206209
}

app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ public function getOptions(array $optionIds, ?int $storeId, array $attributeCode
6464
'attribute_code' => 'a.attribute_code',
6565
'attribute_label' => 'a.frontend_label',
6666
'attribute_type' => 'a.frontend_input',
67-
'position' => 'attribute_configuration.position'
67+
'position' => 'attribute_configuration.position',
68+
'is_filterable' => 'attribute_configuration.is_filterable',
6869
]
6970
)
7071
->joinLeft(
@@ -107,7 +108,7 @@ public function getOptions(array $optionIds, ?int $storeId, array $attributeCode
107108
'options.sort_order ' . Select::SQL_ASC
108109
);
109110

110-
$select->where('option_value.option_id IN (?)', $optionIds);
111+
$select->where('option_value.option_id IN (?) OR attribute_configuration.is_filterable = 2', $optionIds);
111112

112113
if (!empty($attributeCodes)) {
113114
$select->orWhere(
@@ -136,10 +137,10 @@ private function formatResult(Select $select): array
136137
$result[$option['attribute_code']] = [
137138
'attribute_id' => $option['attribute_id'],
138139
'attribute_code' => $option['attribute_code'],
139-
'attribute_label' => $option['attribute_store_label']
140-
? $option['attribute_store_label'] : $option['attribute_label'],
140+
'attribute_label' => $option['attribute_store_label'] ?: $option['attribute_label'],
141141
'attribute_type' => $option['attribute_type'],
142142
'position' => $option['position'],
143+
'is_filterable' => (int) $option['is_filterable'],
143144
'options' => [],
144145
];
145146
}

app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class Attribute implements LayerBuilderInterface
3232
*/
3333
private const CATEGORY_BUCKET = 'category_bucket';
3434

35+
private const ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS = 1;
36+
3537
/**
3638
* @var AttributeOptionProvider
3739
*/
@@ -91,23 +93,21 @@ public function build(AggregationInterface $aggregation, ?int $storeId): array
9193

9294
$result[$bucketName] = $this->layerFormatter->buildLayer(
9395
$attribute['attribute_label'] ?? $bucketName,
94-
\count($bucket->getValues()),
96+
0,
9597
$attribute['attribute_code'] ?? $bucketName,
9698
isset($attribute['position']) ? $attribute['position'] : null
9799
);
98-
99-
$options = $this->getSortedOptions(
100-
$bucket,
101-
isset($attribute['options']) ? $attribute['options'] : [],
102-
($attribute['attribute_type']) ? $attribute['attribute_type']: ''
103-
);
104-
foreach ($options as $option) {
105-
$result[$bucketName]['options'][] = $this->layerFormatter->buildItem(
106-
$option['label'],
107-
$option['value'],
108-
$option['count']
100+
$optionLabels = $attribute['attribute_type'] === 'boolean'
101+
? $this->YesNo->toArray()
102+
: $attribute['options'] ?? [];
103+
$result[$bucketName]['options'] = $this->getSortedOptions($bucket, $optionLabels);
104+
if (self::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS === $attribute['is_filterable']) {
105+
$result[$bucketName]['options'] = array_filter(
106+
$result[$bucketName]['options'],
107+
fn ($option) => $option['count']
109108
);
110109
}
110+
$result[$bucketName]['count'] = count($result[$bucketName]['options']);
111111
}
112112

113113
return $result;
@@ -181,38 +181,24 @@ function (AggregationValueInterface $value) {
181181
*
182182
* @param BucketInterface $bucket
183183
* @param array $optionLabels
184-
* @param string $attributeType
185184
* @return array
186-
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
187185
*/
188-
private function getSortedOptions(BucketInterface $bucket, array $optionLabels, string $attributeType): array
186+
private function getSortedOptions(BucketInterface $bucket, array $optionLabels): array
189187
{
188+
$options = [];
190189
/**
191190
* Option labels array has been sorted
192191
*/
193-
$options = $optionLabels;
192+
foreach ($optionLabels as $optionId => $optionLabel) {
193+
$options[$optionId] = $this->layerFormatter->buildItem($optionLabel, $optionId, 0);
194+
}
194195
foreach ($bucket->getValues() as $value) {
195196
$metrics = $value->getMetrics();
196-
$optionValue = $metrics['value'];
197-
if (isset($optionLabels[$optionValue])) {
198-
$optionLabel = $optionLabels[$optionValue];
197+
$optionId = $metrics['value'];
198+
if (isset($options[$optionId])) {
199+
$options[$optionId]['count'] = $metrics['count'];
199200
} else {
200-
if ($attributeType === 'boolean') {
201-
$yesNoOptions = $this->YesNo->toArray();
202-
$optionLabel = $yesNoOptions[$optionValue];
203-
} else {
204-
$optionLabel = $optionValue;
205-
}
206-
}
207-
$options[$optionValue] = $metrics + ['label' => $optionLabel];
208-
}
209-
210-
/**
211-
* Delete options without bucket values
212-
*/
213-
foreach ($options as $optionId => $option) {
214-
if (!is_array($options[$optionId])) {
215-
unset($options[$optionId]);
201+
$options[$optionId] = $this->layerFormatter->buildItem($optionId, $optionId, $metrics['count']);
216202
}
217203
}
218204

dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchAggregationsTest.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
namespace Magento\GraphQl\Catalog;
99

10+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
11+
use Magento\Catalog\Test\Fixture\SelectAttribute as SelectAttributeFixture;
12+
use Magento\Eav\Test\Fixture\AttributeOption as AttributeOptionFixture;
13+
use Magento\TestFramework\Fixture\DataFixture;
1014
use Magento\TestFramework\TestCase\GraphQlAbstract;
1115

1216
class ProductSearchAggregationsTest extends GraphQlAbstract
@@ -154,6 +158,59 @@ function ($a) {
154158
$this->assertEquals($expectedOptions, $priceAggregation['options']);
155159
}
156160

161+
#[
162+
DataFixture(
163+
SelectAttributeFixture::class,
164+
['attribute_code' => 'attr_with_results', 'options' => [], 'is_filterable' => 1]
165+
),
166+
DataFixture(
167+
AttributeOptionFixture::class,
168+
['entity_type' => 4, 'attribute_code' => 'attr_with_results'],
169+
'attr1opt1'
170+
),
171+
DataFixture(
172+
AttributeOptionFixture::class,
173+
['entity_type' => 4, 'attribute_code' => 'attr_with_results'],
174+
'attr1opt2'
175+
),
176+
DataFixture(
177+
SelectAttributeFixture::class,
178+
['attribute_code' => 'attr_no_results', 'options' => [], 'is_filterable' => 2]
179+
),
180+
DataFixture(
181+
AttributeOptionFixture::class,
182+
['entity_type' => 4, 'attribute_code' => 'attr_no_results'],
183+
'attr2opt1'
184+
),
185+
DataFixture(
186+
AttributeOptionFixture::class,
187+
['entity_type' => 4, 'attribute_code' => 'attr_no_results'],
188+
'attr2opt2'
189+
),
190+
DataFixture(
191+
ProductFixture::class,
192+
[
193+
'sku' => 'simple_product1',
194+
'attr_with_results' => '$attr1opt1.value$',
195+
'attr_no_results' => '$attr2opt1.value$',
196+
]
197+
),
198+
]
199+
public function testAggregationFilterableAttributes(): void
200+
{
201+
$query = $this->getGraphQlQuery('"simple_product1"');
202+
$result = $this->graphQlQuery($query);
203+
204+
self::assertArrayNotHasKey('errors', $result);
205+
self::assertArrayHasKey('aggregations', $result['products']);
206+
207+
$aggregations = array_column($result['products']['aggregations'], null, 'attribute_code');
208+
self::assertEquals(1, $aggregations['attr_with_results']['count']);
209+
self::assertCount(1, $aggregations['attr_with_results']['options']);
210+
self::assertEquals(2, $aggregations['attr_no_results']['count']);
211+
self::assertCount(2, $aggregations['attr_no_results']['options']);
212+
}
213+
157214
/**
158215
* Get GraphQl products query with aggregations
159216
*

0 commit comments

Comments
 (0)