Skip to content
This repository was archived by the owner on Apr 29, 2019. It is now read-only.

Commit e3460a2

Browse files
author
Alex Paliarush
committed
MAGETWO-92773: [GraphQL] Products cannot be fetched in parent/anchor category #89
1 parent c67893b commit e3460a2

File tree

9 files changed

+119
-18
lines changed

9 files changed

+119
-18
lines changed

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

+7-9
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,10 @@
2020
use Magento\Framework\Reflection\DataObjectProcessor;
2121

2222
/**
23-
* Category field resolver, used for GraphQL request processing.
23+
* Resolver for categoriy objects the product is assigned to.
2424
*/
2525
class Category implements ResolverInterface
2626
{
27-
/**
28-
* Product category ids
29-
*/
30-
const PRODUCT_CATEGORY_IDS_KEY = 'category_ids';
31-
3227
/**
3328
* @var Collection
3429
*/
@@ -89,10 +84,13 @@ public function __construct(
8984
*/
9085
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) : Value
9186
{
92-
$this->categoryIds = array_merge($this->categoryIds, $value[self::PRODUCT_CATEGORY_IDS_KEY]);
87+
/** @var \Magento\Catalog\Model\Product $product */
88+
$product = $value['model'];
89+
$categoryIds = $product->getCategoryIds();
90+
$this->categoryIds = array_merge($this->categoryIds, $categoryIds);
9391
$that = $this;
9492

95-
return $this->valueFactory->create(function () use ($that, $value, $info) {
93+
return $this->valueFactory->create(function () use ($that, $categoryIds, $info) {
9694
$categories = [];
9795
if (empty($that->categoryIds)) {
9896
return [];
@@ -104,7 +102,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
104102
}
105103
/** @var CategoryInterface | \Magento\Catalog\Model\Category $item */
106104
foreach ($this->collection as $item) {
107-
if (in_array($item->getId(), $value[$that::PRODUCT_CATEGORY_IDS_KEY])) {
105+
if (in_array($item->getId(), $categoryIds)) {
108106
$categories[$item->getId()] = $this->dataObjectProcessor->buildOutputDataArray(
109107
$item,
110108
CategoryInterface::class

app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public function resolve(
6363
array $args = null
6464
): Value {
6565
$args['filter'] = [
66-
'category_ids' => [
66+
'category_id' => [
6767
'eq' => $value['id']
6868
]
6969
];

app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@ public function getList(
7878
$this->collectionProcessor->process($collection, $searchCriteria, $attributes);
7979

8080
if (!$isChildSearch) {
81-
$visibilityIds
82-
= $isSearch ? $this->visibility->getVisibleInSearchIds() : $this->visibility->getVisibleInCatalogIds();
81+
$visibilityIds = $isSearch
82+
? $this->visibility->getVisibleInSearchIds()
83+
: $this->visibility->getVisibleInCatalogIds();
8384
$collection->setVisibility($visibilityIds);
8485
}
8586
$collection->load();
8687

8788
// Methods that perform extra fetches post-load
88-
$collection->addCategoryIds();
8989
$collection->addMediaGalleryData();
9090
$collection->addOptionsToResult();
9191

app/code/Magento/CatalogGraphQl/Model/Resolver/Products/FilterArgument/ProductEntityAttributesForAst.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ProductEntityAttributesForAst implements FieldEntityAttributesInterface
3333
*/
3434
public function __construct(
3535
ConfigInterface $config,
36-
array $additionalAttributes = ['min_price', 'max_price', 'category_ids']
36+
array $additionalAttributes = ['min_price', 'max_price', 'category_id']
3737
) {
3838
$this->config = $config;
3939
$this->additionalAttributes = $additionalAttributes;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\CatalogGraphQl\Model\Resolver\Products\SearchCriteria\CollectionProcessor\FilterProcessor;
7+
8+
use Magento\Catalog\Model\CategoryFactory;
9+
use Magento\Catalog\Model\ResourceModel\Category as CategoryResourceModel;
10+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
11+
use Magento\Framework\Api\Filter;
12+
use Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor\CustomFilterInterface;
13+
use Magento\Framework\Data\Collection\AbstractDb;
14+
use Magento\Framework\Exception\LocalizedException;
15+
16+
/**
17+
* Category filter allows to filter products collection using 'category_id' filter from search criteria.
18+
*/
19+
class CategoryFilter implements CustomFilterInterface
20+
{
21+
/**
22+
* @var CategoryFactory
23+
*/
24+
private $categoryFactory;
25+
26+
/**
27+
* @var CategoryResourceModel
28+
*/
29+
private $categoryResourceModel;
30+
31+
/**
32+
* @param CategoryFactory $categoryFactory
33+
* @param CategoryResourceModel $categoryResourceModel
34+
*/
35+
public function __construct(
36+
CategoryFactory $categoryFactory,
37+
CategoryResourceModel $categoryResourceModel
38+
) {
39+
$this->categoryFactory = $categoryFactory;
40+
$this->categoryResourceModel = $categoryResourceModel;
41+
}
42+
43+
/**
44+
* Apply filter by 'category_id' to product collection.
45+
*
46+
* For anchor categories, the products from all children categories will be present in the result.
47+
*
48+
* @param Filter $filter
49+
* @param AbstractDb $collection
50+
* @return bool Whether the filter is applied
51+
* @throws LocalizedException
52+
*/
53+
public function apply(Filter $filter, AbstractDb $collection)
54+
{
55+
$conditionType = $filter->getConditionType();
56+
57+
if ($conditionType !== 'eq') {
58+
throw new LocalizedException(__("'category_id' only supports 'eq' condition type."));
59+
}
60+
61+
$categoryId = $filter->getValue();
62+
/** @var Collection $collection */
63+
$category = $this->categoryFactory->create();
64+
$this->categoryResourceModel->load($category, $categoryId);
65+
$collection->addCategoryFilter($category);
66+
67+
return true;
68+
}
69+
}

app/code/Magento/CatalogGraphQl/etc/graphql/di.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
<item name="price" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductPriceFilter</item>
7070
<item name="min_price" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductPriceFilter</item>
7171
<item name="max_price" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductPriceFilter</item>
72-
<item name="category_ids" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductCategoryFilter</item>
72+
<item name="category_id" xsi:type="object">Magento\CatalogGraphQl\Model\Resolver\Products\SearchCriteria\CollectionProcessor\FilterProcessor\CategoryFilter</item>
7373
</argument>
7474
</arguments>
7575
</virtualType>

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ input ProductFilterInput @doc(description: "ProductFilterInput defines the filte
441441
min_price: FilterTypeInput @doc(description:"The numeric minimal price of the product. Do not include the currency code.")
442442
max_price: FilterTypeInput @doc(description:"The numeric maximal price of the product. Do not include the currency code.")
443443
special_price: FilterTypeInput @doc(description:"The numeric special price of the product. Do not include the currency code.")
444-
category_ids: FilterTypeInput @doc(description: "An array of category IDs the product belongs to")
444+
category_id: FilterTypeInput @doc(description: "Category ID the product belongs to")
445445
options_container: FilterTypeInput @doc(description: "If the product has multiple options, determines where they appear on the product page")
446446
required_options: FilterTypeInput @doc(description: "Indicates whether the product has required options")
447447
has_options: FilterTypeInput @doc(description: "Indicates whether additional attributes have been created for the product")

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

+35-1
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,6 @@ public function testCategoryProducts()
254254
default_group_id
255255
is_default
256256
}
257-
258257
}
259258
}
260259
}
@@ -281,6 +280,41 @@ public function testCategoryProducts()
281280
$this->assertWebsites($firstProduct, $response['category']['products']['items'][0]['websites']);
282281
}
283282

283+
/**
284+
* @magentoApiDataFixture Magento/Catalog/_files/categories.php
285+
*/
286+
public function testAnchorCategory()
287+
{
288+
$categoryId = 3;
289+
$query = <<<QUERY
290+
{
291+
category(id: {$categoryId}) {
292+
products(sort: {sku: ASC}) {
293+
total_count
294+
items {
295+
sku
296+
}
297+
}
298+
}
299+
}
300+
QUERY;
301+
302+
$response = $this->graphQlQuery($query);
303+
$expectedResponse = [
304+
'category' => [
305+
'products' => [
306+
'total_count' => 3,
307+
'items' => [
308+
['sku' => '12345'],
309+
['sku' => 'simple'],
310+
['sku' => 'simple-4']
311+
]
312+
]
313+
]
314+
];
315+
$this->assertEquals($expectedResponse, $response);
316+
}
317+
284318
/**
285319
* @param ProductInterface $product
286320
* @param array $actualResponse

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ public function testFilterProductsByCategoryIds()
679679
products(
680680
filter:
681681
{
682-
category_ids:{eq:"{$queryCategoryId}"}
682+
category_id:{eq:"{$queryCategoryId}"}
683683
}
684684
pageSize:2
685685

0 commit comments

Comments
 (0)