Skip to content

Commit

Permalink
MAGETWO-92773: [GraphQL] Products cannot be fetched in parent/anchor …
Browse files Browse the repository at this point in the history
…category #89
  • Loading branch information
Alex Paliarush committed Jun 13, 2018
1 parent c67893b commit e3460a2
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 18 deletions.
16 changes: 7 additions & 9 deletions app/code/Magento/CatalogGraphQl/Model/Resolver/Category.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,10 @@
use Magento\Framework\Reflection\DataObjectProcessor;

/**
* Category field resolver, used for GraphQL request processing.
* Resolver for categoriy objects the product is assigned to.
*/
class Category implements ResolverInterface
{
/**
* Product category ids
*/
const PRODUCT_CATEGORY_IDS_KEY = 'category_ids';

/**
* @var Collection
*/
Expand Down Expand Up @@ -89,10 +84,13 @@ public function __construct(
*/
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) : Value
{
$this->categoryIds = array_merge($this->categoryIds, $value[self::PRODUCT_CATEGORY_IDS_KEY]);
/** @var \Magento\Catalog\Model\Product $product */
$product = $value['model'];
$categoryIds = $product->getCategoryIds();
$this->categoryIds = array_merge($this->categoryIds, $categoryIds);
$that = $this;

return $this->valueFactory->create(function () use ($that, $value, $info) {
return $this->valueFactory->create(function () use ($that, $categoryIds, $info) {
$categories = [];
if (empty($that->categoryIds)) {
return [];
Expand All @@ -104,7 +102,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
}
/** @var CategoryInterface | \Magento\Catalog\Model\Category $item */
foreach ($this->collection as $item) {
if (in_array($item->getId(), $value[$that::PRODUCT_CATEGORY_IDS_KEY])) {
if (in_array($item->getId(), $categoryIds)) {
$categories[$item->getId()] = $this->dataObjectProcessor->buildOutputDataArray(
$item,
CategoryInterface::class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function resolve(
array $args = null
): Value {
$args['filter'] = [
'category_ids' => [
'category_id' => [
'eq' => $value['id']
]
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ public function getList(
$this->collectionProcessor->process($collection, $searchCriteria, $attributes);

if (!$isChildSearch) {
$visibilityIds
= $isSearch ? $this->visibility->getVisibleInSearchIds() : $this->visibility->getVisibleInCatalogIds();
$visibilityIds = $isSearch
? $this->visibility->getVisibleInSearchIds()
: $this->visibility->getVisibleInCatalogIds();
$collection->setVisibility($visibilityIds);
}
$collection->load();

// Methods that perform extra fetches post-load
$collection->addCategoryIds();
$collection->addMediaGalleryData();
$collection->addOptionsToResult();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ProductEntityAttributesForAst implements FieldEntityAttributesInterface
*/
public function __construct(
ConfigInterface $config,
array $additionalAttributes = ['min_price', 'max_price', 'category_ids']
array $additionalAttributes = ['min_price', 'max_price', 'category_id']
) {
$this->config = $config;
$this->additionalAttributes = $additionalAttributes;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\CatalogGraphQl\Model\Resolver\Products\SearchCriteria\CollectionProcessor\FilterProcessor;

use Magento\Catalog\Model\CategoryFactory;
use Magento\Catalog\Model\ResourceModel\Category as CategoryResourceModel;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Framework\Api\Filter;
use Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor\CustomFilterInterface;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Exception\LocalizedException;

/**
* Category filter allows to filter products collection using 'category_id' filter from search criteria.
*/
class CategoryFilter implements CustomFilterInterface
{
/**
* @var CategoryFactory
*/
private $categoryFactory;

/**
* @var CategoryResourceModel
*/
private $categoryResourceModel;

/**
* @param CategoryFactory $categoryFactory
* @param CategoryResourceModel $categoryResourceModel
*/
public function __construct(
CategoryFactory $categoryFactory,
CategoryResourceModel $categoryResourceModel
) {
$this->categoryFactory = $categoryFactory;
$this->categoryResourceModel = $categoryResourceModel;
}

/**
* Apply filter by 'category_id' to product collection.
*
* For anchor categories, the products from all children categories will be present in the result.
*
* @param Filter $filter
* @param AbstractDb $collection
* @return bool Whether the filter is applied
* @throws LocalizedException
*/
public function apply(Filter $filter, AbstractDb $collection)
{
$conditionType = $filter->getConditionType();

if ($conditionType !== 'eq') {
throw new LocalizedException(__("'category_id' only supports 'eq' condition type."));
}

$categoryId = $filter->getValue();
/** @var Collection $collection */
$category = $this->categoryFactory->create();
$this->categoryResourceModel->load($category, $categoryId);
$collection->addCategoryFilter($category);

return true;
}
}
2 changes: 1 addition & 1 deletion app/code/Magento/CatalogGraphQl/etc/graphql/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
<item name="price" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductPriceFilter</item>
<item name="min_price" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductPriceFilter</item>
<item name="max_price" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductPriceFilter</item>
<item name="category_ids" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductCategoryFilter</item>
<item name="category_id" xsi:type="object">Magento\CatalogGraphQl\Model\Resolver\Products\SearchCriteria\CollectionProcessor\FilterProcessor\CategoryFilter</item>
</argument>
</arguments>
</virtualType>
Expand Down
2 changes: 1 addition & 1 deletion app/code/Magento/CatalogGraphQl/etc/schema.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ input ProductFilterInput @doc(description: "ProductFilterInput defines the filte
min_price: FilterTypeInput @doc(description:"The numeric minimal price of the product. Do not include the currency code.")
max_price: FilterTypeInput @doc(description:"The numeric maximal price of the product. Do not include the currency code.")
special_price: FilterTypeInput @doc(description:"The numeric special price of the product. Do not include the currency code.")
category_ids: FilterTypeInput @doc(description: "An array of category IDs the product belongs to")
category_id: FilterTypeInput @doc(description: "Category ID the product belongs to")
options_container: FilterTypeInput @doc(description: "If the product has multiple options, determines where they appear on the product page")
required_options: FilterTypeInput @doc(description: "Indicates whether the product has required options")
has_options: FilterTypeInput @doc(description: "Indicates whether additional attributes have been created for the product")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ public function testCategoryProducts()
default_group_id
is_default
}
}
}
}
Expand All @@ -281,6 +280,41 @@ public function testCategoryProducts()
$this->assertWebsites($firstProduct, $response['category']['products']['items'][0]['websites']);
}

/**
* @magentoApiDataFixture Magento/Catalog/_files/categories.php
*/
public function testAnchorCategory()
{
$categoryId = 3;
$query = <<<QUERY
{
category(id: {$categoryId}) {
products(sort: {sku: ASC}) {
total_count
items {
sku
}
}
}
}
QUERY;

$response = $this->graphQlQuery($query);
$expectedResponse = [
'category' => [
'products' => [
'total_count' => 3,
'items' => [
['sku' => '12345'],
['sku' => 'simple'],
['sku' => 'simple-4']
]
]
]
];
$this->assertEquals($expectedResponse, $response);
}

/**
* @param ProductInterface $product
* @param array $actualResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ public function testFilterProductsByCategoryIds()
products(
filter:
{
category_ids:{eq:"{$queryCategoryId}"}
category_id:{eq:"{$queryCategoryId}"}
}
pageSize:2
Expand Down

0 comments on commit e3460a2

Please sign in to comment.