Skip to content

Commit 7e71f44

Browse files
committed
Merge remote-tracking branch 'magento-l3/MC-42795' into L3_PR_21-10-19
2 parents 53434ab + cd6accf commit 7e71f44

File tree

5 files changed

+328
-3
lines changed

5 files changed

+328
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Aggregations\Category;
9+
10+
use Magento\Catalog\Api\CategoryListInterface;
11+
use Magento\Framework\Api\SearchCriteriaBuilder;
12+
use Magento\Framework\Search\Response\Aggregation;
13+
use Magento\Framework\Search\Response\AggregationFactory;
14+
use Magento\Framework\Search\Response\BucketFactory;
15+
use Magento\Store\Model\StoreManagerInterface;
16+
use Magento\Framework\Api\Search\AggregationInterface;
17+
18+
/**
19+
* Class to include only direct subcategories of category in aggregation
20+
*/
21+
class IncludeDirectChildrenOnly
22+
{
23+
/**
24+
* @var string
25+
*/
26+
private const CATEGORY_BUCKET = 'category_bucket';
27+
28+
/**
29+
* @var string
30+
*/
31+
private const BUCKETS_NAME = 'buckets';
32+
33+
/**
34+
* @var AggregationFactory
35+
*/
36+
private $aggregationFactory;
37+
38+
/**
39+
* @var BucketFactory
40+
*/
41+
private $bucketFactory;
42+
43+
/**
44+
* @var StoreManagerInterface
45+
*/
46+
private $storeManager;
47+
48+
/**
49+
* @var CategoryListInterface
50+
*/
51+
private $categoryList;
52+
53+
/**
54+
* @var array
55+
*/
56+
private $filter = [];
57+
58+
/**
59+
* @var SearchCriteriaBuilder
60+
*/
61+
private $searchCriteriaBuilder;
62+
63+
/**
64+
* @param AggregationFactory $aggregationFactory
65+
* @param BucketFactory $bucketFactory
66+
* @param StoreManagerInterface $storeManager
67+
* @param CategoryListInterface $categoryList
68+
* @param SearchCriteriaBuilder $searchCriteriaBuilder
69+
*/
70+
public function __construct(
71+
AggregationFactory $aggregationFactory,
72+
BucketFactory $bucketFactory,
73+
StoreManagerInterface $storeManager,
74+
CategoryListInterface $categoryList,
75+
SearchCriteriaBuilder $searchCriteriaBuilder
76+
) {
77+
$this->aggregationFactory = $aggregationFactory;
78+
$this->bucketFactory = $bucketFactory;
79+
$this->storeManager = $storeManager;
80+
$this->categoryList = $categoryList;
81+
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
82+
}
83+
84+
/**
85+
* Filter category aggregation to include only direct subcategories of requested category
86+
*
87+
* @param AggregationInterface $aggregation
88+
* @param int|null $storeId
89+
* @return Aggregation
90+
*/
91+
public function filter(AggregationInterface $aggregation, ?int $storeId): Aggregation
92+
{
93+
$categoryIdsRequested = $this->filter['category'] ?? null;
94+
if ($categoryIdsRequested === null) {
95+
return $aggregation;
96+
}
97+
$buckets = $aggregation->getBuckets();
98+
$categoryBucket = $buckets[self::CATEGORY_BUCKET] ?? null;
99+
if ($categoryBucket === null || empty($categoryBucket->getValues())) {
100+
return $aggregation;
101+
}
102+
$categoryIdsRequested = is_array($categoryIdsRequested) ? $categoryIdsRequested : [$categoryIdsRequested];
103+
$bucketValuesFiltered = $this->filterBucketValues(
104+
$categoryBucket->getValues(),
105+
$categoryIdsRequested,
106+
$storeId
107+
);
108+
$categoryBucketResolved = $this->bucketFactory->create(
109+
[
110+
'name' => self::CATEGORY_BUCKET,
111+
'values' => $bucketValuesFiltered
112+
]
113+
);
114+
$buckets[self::CATEGORY_BUCKET] = $categoryBucketResolved;
115+
return $this->aggregationFactory->create([self::BUCKETS_NAME => $buckets]);
116+
}
117+
118+
/**
119+
* Set filter for categories aggregation
120+
*
121+
* @param array $filter
122+
*/
123+
public function setFilter(array $filter): void
124+
{
125+
$this->filter = $filter;
126+
}
127+
128+
/**
129+
* Filter bucket values to include only direct subcategories of requested category
130+
*
131+
* @param array $categoryBucketValues
132+
* @param array $categoryIdsRequested
133+
* @param int|null $storeId
134+
* @return array
135+
*/
136+
private function filterBucketValues(
137+
array $categoryBucketValues,
138+
array $categoryIdsRequested,
139+
?int $storeId
140+
): array {
141+
$categoryChildIds = [];
142+
$storeId = $storeId !== null ? $storeId : $this->storeManager->getStore()->getId();
143+
$searchCriteria = $this->searchCriteriaBuilder
144+
->addFilter('entity_id', $categoryIdsRequested, 'in')
145+
->create();
146+
$categoriesRequested = $this->categoryList->getList($searchCriteria);
147+
foreach ($categoriesRequested->getItems() as $category) {
148+
$category->setStoreId($storeId);
149+
$childrenIds = $category->getChildren();
150+
if ($childrenIds) {
151+
$categoryChildIds[] = explode(',', $childrenIds);
152+
}
153+
}
154+
$categoryChildIds = array_merge([], ...$categoryChildIds);
155+
foreach ($categoryBucketValues as $key => $bucketValue) {
156+
$categoryId = (int)$bucketValue->getValue();
157+
if (!in_array($categoryId, $categoryChildIds)) {
158+
unset($categoryBucketValues[$key]);
159+
}
160+
}
161+
return array_values($categoryBucketValues);
162+
}
163+
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Magento\Framework\Api\Search\BucketInterface;
1717
use Magento\Framework\App\ResourceConnection;
1818
use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Formatter\LayerFormatter;
19+
use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Aggregations;
1920

2021
/**
2122
* @inheritdoc
@@ -62,25 +63,33 @@ class Category implements LayerBuilderInterface
6263
*/
6364
private $layerFormatter;
6465

66+
/**
67+
* @var Aggregations\Category\IncludeDirectChildrenOnly
68+
*/
69+
private $includeDirectChildrenOnly;
70+
6571
/**
6672
* @param CategoryAttributeQuery $categoryAttributeQuery
6773
* @param CategoryAttributesMapper $attributesMapper
6874
* @param RootCategoryProvider $rootCategoryProvider
6975
* @param ResourceConnection $resourceConnection
7076
* @param LayerFormatter $layerFormatter
77+
* @param Aggregations\Category\IncludeDirectChildrenOnly $includeDirectChildrenOnly
7178
*/
7279
public function __construct(
7380
CategoryAttributeQuery $categoryAttributeQuery,
7481
CategoryAttributesMapper $attributesMapper,
7582
RootCategoryProvider $rootCategoryProvider,
7683
ResourceConnection $resourceConnection,
77-
LayerFormatter $layerFormatter
84+
LayerFormatter $layerFormatter,
85+
Aggregations\Category\IncludeDirectChildrenOnly $includeDirectChildrenOnly
7886
) {
7987
$this->categoryAttributeQuery = $categoryAttributeQuery;
8088
$this->attributesMapper = $attributesMapper;
8189
$this->resourceConnection = $resourceConnection;
8290
$this->rootCategoryProvider = $rootCategoryProvider;
8391
$this->layerFormatter = $layerFormatter;
92+
$this->includeDirectChildrenOnly = $includeDirectChildrenOnly;
8493
}
8594

8695
/**
@@ -90,6 +99,7 @@ public function __construct(
9099
*/
91100
public function build(AggregationInterface $aggregation, ?int $storeId): array
92101
{
102+
$aggregation = $this->includeDirectChildrenOnly->filter($aggregation, $storeId);
93103
$bucket = $aggregation->getBucket(self::CATEGORY_BUCKET);
94104
if ($this->isBucketEmpty($bucket)) {
95105
return [];

app/code/Magento/CatalogGraphQl/Model/Resolver/Aggregations.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Magento\CatalogGraphQl\Model\Resolver;
99

1010
use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\LayerBuilder;
11+
use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Aggregations\Category;
1112
use Magento\Directory\Model\PriceCurrency;
1213
use Magento\Framework\App\ObjectManager;
1314
use Magento\Framework\GraphQl\Config\Element\Field;
@@ -35,19 +36,28 @@ class Aggregations implements ResolverInterface
3536
*/
3637
private $priceCurrency;
3738

39+
/**
40+
* @var Category\IncludeDirectChildrenOnly
41+
*/
42+
private $includeDirectChildrenOnly;
43+
3844
/**
3945
* @param \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider
4046
* @param LayerBuilder $layerBuilder
4147
* @param PriceCurrency $priceCurrency
48+
* @param Category\IncludeDirectChildrenOnly $includeDirectChildrenOnly
4249
*/
4350
public function __construct(
4451
\Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider,
4552
LayerBuilder $layerBuilder,
46-
PriceCurrency $priceCurrency = null
53+
PriceCurrency $priceCurrency = null,
54+
Category\IncludeDirectChildrenOnly $includeDirectChildrenOnly = null
4755
) {
4856
$this->filtersDataProvider = $filtersDataProvider;
4957
$this->layerBuilder = $layerBuilder;
5058
$this->priceCurrency = $priceCurrency ?: ObjectManager::getInstance()->get(PriceCurrency::class);
59+
$this->includeDirectChildrenOnly = $includeDirectChildrenOnly
60+
?: ObjectManager::getInstance()->get(Category\IncludeDirectChildrenOnly::class);
5161
}
5262

5363
/**
@@ -67,6 +77,11 @@ public function resolve(
6777
$aggregations = $value['search_result']->getSearchAggregation();
6878

6979
if ($aggregations) {
80+
$categoryFilter = $value['categories'] ?? [];
81+
$includeDirectChildrenOnly = $args['filter']['category']['includeDirectChildrenOnly'] ?? false;
82+
if ($includeDirectChildrenOnly && !empty($categoryFilter)) {
83+
$this->includeDirectChildrenOnly->setFilter(['category' => $categoryFilter]);
84+
}
7085
/** @var StoreInterface $store */
7186
$store = $context->getExtensionAttributes()->getStore();
7287
$storeId = (int)$store->getId();

app/code/Magento/CatalogGraphQl/etc/schema.graphqls

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,19 @@ type Products @doc(description: "The Products object is the top-level object ret
320320
page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query.")
321321
total_count: Int @doc(description: "The number of products that are marked as visible. By default, in complex products, parent products are visible, but their child products are not.")
322322
filters: [LayerFilter] @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\LayerFilters") @doc(description: "Layered navigation filters array.") @deprecated(reason: "Use aggregations instead")
323-
aggregations: [Aggregation] @doc(description: "Layered navigation aggregations.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Aggregations")
323+
aggregations (filter: AggregationsFilterInput): [Aggregation] @doc(description: "Layered navigation aggregations with filters.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Aggregations")
324324
sort_fields: SortFields @doc(description: "An object that includes the default sort field and all available sort fields.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFields")
325325
suggestions: [SearchSuggestion] @doc(description: "An array of search suggestions for case when search query have no results.")
326326
}
327327

328+
input AggregationsFilterInput @doc(description: "An input object that specifies the filters used in product aggregations.") {
329+
category: AggregationsCategoryFilterInput @doc(description: "Filter category aggregations in layered navigation.")
330+
}
331+
332+
input AggregationsCategoryFilterInput @doc(description: "Filter category aggregations in layered navigation."){
333+
includeDirectChildrenOnly: Boolean = false @doc(description: "Indicates whether to include only direct subcategories or all children categories at all levels.")
334+
}
335+
328336
type CategoryProducts @doc(description: "The category products object returned in the Category query.") {
329337
items: [ProductInterface] @doc(description: "An array of products that are assigned to the category.")
330338
page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query.")

0 commit comments

Comments
 (0)