Skip to content

Commit 258da02

Browse files
committed
ACP2E-1652: Unable to fetch url_key value from products GraphQL query when there's multiple category selection
1 parent 405bee6 commit 258da02

File tree

2 files changed

+157
-73
lines changed

2 files changed

+157
-73
lines changed

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

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
class Categories implements ResolverInterface
2828
{
2929
/**
30-
* @var Collection
30+
* @var CollectionFactory
3131
*/
32-
private $collection;
32+
private $collectionFactory;
3333

3434
/**
3535
* Accumulated category ids
@@ -68,6 +68,11 @@ class Categories implements ResolverInterface
6868
*/
6969
private $storeManager;
7070

71+
/**
72+
* @var array
73+
*/
74+
private $collections = [];
75+
7176
/**
7277
* @param CollectionFactory $collectionFactory
7378
* @param AttributesJoiner $attributesJoiner
@@ -86,7 +91,7 @@ public function __construct(
8691
ProductCategories $productCategories,
8792
StoreManagerInterface $storeManager
8893
) {
89-
$this->collection = $collectionFactory->create();
94+
$this->collectionFactory = $collectionFactory;
9095
$this->attributesJoiner = $attributesJoiner;
9196
$this->customAttributesFlattener = $customAttributesFlattener;
9297
$this->valueFactory = $valueFactory;
@@ -120,12 +125,9 @@ function () use ($that, $categoryIds, $info) {
120125
return [];
121126
}
122127

123-
if (!$this->collection->isLoaded()) {
124-
$that->attributesJoiner->join($info->fieldNodes[0], $this->collection, $info);
125-
$this->collection->addIdFilter($this->categoryIds);
126-
}
128+
$collection = $this->getCollection($that, $info);
127129
/** @var CategoryInterface | \Magento\Catalog\Model\Category $item */
128-
foreach ($this->collection as $item) {
130+
foreach ($collection as $item) {
129131
if (in_array($item->getId(), $categoryIds)) {
130132
// Try to extract all requested fields from the loaded collection data
131133
$categories[$item->getId()] = $this->categoryHydrator->hydrateCategory($item, true);
@@ -146,4 +148,25 @@ function () use ($that, $categoryIds, $info) {
146148
}
147149
);
148150
}
151+
152+
/**
153+
* Returns category collection.
154+
*
155+
* @param Categories $that
156+
* @param ResolveInfo $info
157+
* @return Collection
158+
*/
159+
private function getCollection(Categories $that, ResolveInfo $info): Collection
160+
{
161+
$requestedFields = $that->attributesJoiner->getQueryFields($info->fieldNodes[0], $info);
162+
sort($requestedFields);
163+
$requestedFieldsHash = sha1(implode(',', $requestedFields));
164+
if (!isset($this->collections[$requestedFieldsHash])) {
165+
$this->collections[$requestedFieldsHash] = $this->collectionFactory->create();
166+
$that->attributesJoiner->join($info->fieldNodes[0], $this->collections[$requestedFieldsHash], $info);
167+
$this->collections[$requestedFieldsHash]->addIdFilter($this->categoryIds);
168+
}
169+
170+
return $this->collections[$requestedFieldsHash];
171+
}
149172
}

dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogGraphQl/ProductSearchTest.php

Lines changed: 126 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
namespace Magento\GraphQl\CatalogGraphQl;
99

10+
use Magento\Catalog\Test\Fixture\Category as CategoryFixture;
11+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
12+
use Magento\Indexer\Model\IndexerFactory;
1013
use Magento\TestFramework\Helper\Bootstrap;
1114
use Magento\TestFramework\TestCase\GraphQlAbstract;
1215
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
@@ -20,17 +23,125 @@
2023
*/
2124
class ProductSearchTest extends GraphQlAbstract
2225
{
26+
#[
27+
DataFixture(CategoryFixture::class, as: 'cat1'),
28+
DataFixture(
29+
ProductFixture::class,
30+
[
31+
'category_ids' => ['$cat1.id$'],
32+
],
33+
'product'
34+
)
35+
]
36+
public function testSearchProductsWithCategoriesAliasPresentInQuery(): void
37+
{
38+
$this->reindexCatalogCategory();
39+
/** @var \Magento\Catalog\Model\Product $product */
40+
$product = DataFixtureStorageManager::getStorage()->get('product');
41+
/** @var \Magento\Catalog\Model\Category $category */
42+
$category = DataFixtureStorageManager::getStorage()->get('cat1');
43+
$response = $this->graphQlQuery($this->getProductSearchQueryWithCategoriesAlias($product->getSku()));
44+
45+
$this->assertNotEmpty($response['products']);
46+
$this->assertNotEmpty($response['products']['items']);
47+
$this->assertEquals(
48+
$category->getUrlKey(),
49+
$response['products']['items'][0]['custom_categories'][0]['url_key']
50+
);
51+
}
52+
2353
/**
24-
* @var ObjectManager|null
54+
* Make catalog_category reindex.
55+
*
56+
* @return void
57+
* @throws \Throwable
2558
*/
26-
private $objectManager;
59+
private function reindexCatalogCategory(): void
60+
{
61+
$indexerFactory = Bootstrap::getObjectManager()->create(IndexerFactory::class);
62+
$indexer = $indexerFactory->create();
63+
$indexer->load('catalog_category_product')->reindexAll();
64+
}
65+
66+
#[
67+
DataFixture(Product::class, as: 'product')
68+
]
69+
public function testSearchProductsWithSkuEqFilterQuery(): void
70+
{
71+
/** @var \Magento\Catalog\Model\Product $product */
72+
$product = DataFixtureStorageManager::getStorage()->get('product');
73+
$response = $this->graphQlQuery($this->getProductSearchQuery($product->getName(), $product->getSku()));
74+
75+
$this->assertNotEmpty($response['products']);
76+
$this->assertEquals(1, $response['products']['total_count']);
77+
$this->assertNotEmpty($response['products']['items']);
78+
$this->assertEquals($product->getName(), $response['products']['items'][0]['name']);
79+
$this->assertEquals($product->getSku(), $response['products']['items'][0]['sku']);
80+
}
81+
82+
#[
83+
DataFixture(Product::class, as: 'product1'),
84+
DataFixture(Product::class, as: 'product2'),
85+
DataFixture(Product::class, as: 'product3')
86+
]
87+
public function testSearchProductsWithMultipleSkuInFilterQuery(): void
88+
{
89+
/** @var \Magento\Catalog\Model\Product $product */
90+
$response = $this->graphQlQuery(
91+
$this->getProductSearchQueryWithMultipleSkusFilter([
92+
DataFixtureStorageManager::getStorage()->get('product1'),
93+
DataFixtureStorageManager::getStorage()->get('product2'),
94+
DataFixtureStorageManager::getStorage()->get('product3')
95+
], "simple")
96+
);
97+
98+
$this->assertNotEmpty($response['products']);
99+
$this->assertEquals(3, $response['products']['total_count']);
100+
$this->assertNotEmpty($response['products']['items']);
101+
}
102+
103+
#[
104+
DataFixture(Product::class, as: 'product1'),
105+
DataFixture(Product::class, as: 'product2'),
106+
DataFixture(Indexer::class, as: 'indexer')
107+
]
108+
public function testSearchSuggestions(): void
109+
{
110+
$response = $this->graphQlQuery($this->getSearchQueryWithSuggestions());
111+
$this->assertNotEmpty($response['products']);
112+
$this->assertEmpty($response['products']['items']);
113+
$this->assertNotEmpty($response['products']['suggestions']);
114+
}
27115

28116
/**
29-
* Test setup
117+
* Get a query which contains alias for product categories data.
118+
*
119+
* @param string $productSku
120+
* @return string
30121
*/
31-
protected function setUp(): void
122+
private function getProductSearchQueryWithCategoriesAlias(string $productSku): string
32123
{
33-
$this->objectManager = Bootstrap::getObjectManager();
124+
return <<<QUERY
125+
{
126+
products(filter: {
127+
sku: {
128+
eq: "{$productSku}"
129+
}})
130+
{
131+
items {
132+
name
133+
sku
134+
categories {
135+
uid
136+
name
137+
}
138+
custom_categories: categories {
139+
url_key
140+
}
141+
}
142+
}
143+
}
144+
QUERY;
34145
}
35146

36147
/**
@@ -47,11 +158,11 @@ private function getProductSearchQuery(string $productName, string $productSku):
47158
products(filter: {
48159
sku: {
49160
eq: "{$productSku}"
50-
}},
51-
search: "$productName",
52-
sort: {},
53-
pageSize: 200,
54-
currentPage: 1)
161+
}},
162+
search: "$productName",
163+
sort: {},
164+
pageSize: 200,
165+
currentPage: 1)
55166
{
56167
total_count
57168
page_info {
@@ -86,11 +197,11 @@ private function getProductSearchQueryWithMultipleSkusFilter(array $products, st
86197
"{$products[1]->getSku()}",
87198
"{$products[2]->getSku()}"
88199
]
89-
}},
90-
search: "$product",
91-
sort: {},
92-
pageSize: 200,
93-
currentPage: 1)
200+
}},
201+
search: "$product",
202+
sort: {},
203+
pageSize: 200,
204+
currentPage: 1)
94205
{
95206
total_count
96207
page_info {
@@ -130,54 +241,4 @@ private function getSearchQueryWithSuggestions(): string
130241
}
131242
QUERY;
132243
}
133-
134-
#[
135-
DataFixture(Product::class, as: 'product')
136-
]
137-
public function testSearchProductsWithSkuEqFilterQuery(): void
138-
{
139-
/** @var \Magento\Catalog\Model\Product $product */
140-
$product = DataFixtureStorageManager::getStorage()->get('product');
141-
$response = $this->graphQlQuery($this->getProductSearchQuery($product->getName(), $product->getSku()));
142-
143-
$this->assertNotEmpty($response['products']);
144-
$this->assertEquals(1, $response['products']['total_count']);
145-
$this->assertNotEmpty($response['products']['items']);
146-
$this->assertEquals($product->getName(), $response['products']['items'][0]['name']);
147-
$this->assertEquals($product->getSku(), $response['products']['items'][0]['sku']);
148-
}
149-
150-
#[
151-
DataFixture(Product::class, as: 'product1'),
152-
DataFixture(Product::class, as: 'product2'),
153-
DataFixture(Product::class, as: 'product3')
154-
]
155-
public function testSearchProductsWithMultipleSkuInFilterQuery(): void
156-
{
157-
/** @var \Magento\Catalog\Model\Product $product */
158-
$response = $this->graphQlQuery(
159-
$this->getProductSearchQueryWithMultipleSkusFilter([
160-
DataFixtureStorageManager::getStorage()->get('product1'),
161-
DataFixtureStorageManager::getStorage()->get('product2'),
162-
DataFixtureStorageManager::getStorage()->get('product3')
163-
], "simple")
164-
);
165-
166-
$this->assertNotEmpty($response['products']);
167-
$this->assertEquals(3, $response['products']['total_count']);
168-
$this->assertNotEmpty($response['products']['items']);
169-
}
170-
171-
#[
172-
DataFixture(Product::class, as: 'product1'),
173-
DataFixture(Product::class, as: 'product2'),
174-
DataFixture(Indexer::class, as: 'indexer')
175-
]
176-
public function testSearchSuggestions(): void
177-
{
178-
$response = $this->graphQlQuery($this->getSearchQueryWithSuggestions());
179-
$this->assertNotEmpty($response['products']);
180-
$this->assertEmpty($response['products']['items']);
181-
$this->assertNotEmpty($response['products']['suggestions']);
182-
}
183244
}

0 commit comments

Comments
 (0)