magento2: Position sort does not work in GraphQl.

Preconditions (*)

  1. Magento 2.3.4 Open Source

Steps to reproduce (*)

  1. Install demo data
  2. Assign positions to all products in the Watches category (id: 6) Screenshot from 2020-02-11 16-31-53
  3. Run the following GraphQl Query.
query category {
  categoryList(
    filters: {ids: {eq: "6"}}
  ) {
    products(
      sort: {
        position: ASC
      }
    ) {
      items {
        id
      }
      total_count
    }
  }
}

Expected result (*)

{
    "data": {
        "categoryList": [
            {
                "products": {
                    "items": [
                        {
                            "id": 43
                        },
                        {
                            "id": 41
                        },
                        {
                            "id": 39
                        },
                        {
                            "id": 37
                        },
                        {
                            "id": 36
                        },
                        {
                            "id": 38
                        },
                        {
                            "id": 40
                        },
                        {
                            "id": 42
                        },
                        {
                            "id": 44
                        }
                    ],
                    "total_count": 9
                }
            }
        ]
    }
}

Actual result (*)

{
    "data": {
        "categoryList": [
            {
                "products": {
                    "items": [
                        {
                            "id": 36
                        },
                        {
                            "id": 37
                        },
                        {
                            "id": 38
                        },
                        {
                            "id": 39
                        },
                        {
                            "id": 40
                        },
                        {
                            "id": 41
                        },
                        {
                            "id": 42
                        },
                        {
                            "id": 43
                        },
                        {
                            "id": 44
                        }
                    ],
                    "total_count": 9
                }
            }
        ]
    }
}

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 22 (5 by maintainers)

Most upvoted comments

I’m using magento 2.3.5-p1, and this is how i fixed it based on @radub and @Hexmage answers and using magento plugins:

in Vendor/etc/di.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch">
        <plugin name="vendor_category_product_search_after" type="Vendor\CategoryProductsSortFix\Plugin\BeforeProductSearch"/>
    </type>
    <type name="Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CompositeCollectionProcessor">
        <arguments>
            <argument name="collectionProcessors" xsi:type="array">
                <item name="joinCatalogCategoryProductForPosition" xsi:type="object">
                    Vendor\CategoryProductsSortFix\Model\Resolver\Products\DataProvider\CatalogCategoryProductJoinProcessor
                </item>
            </argument>
        </arguments>
    </type>
</config>

in Vendor/CategoryProductsSortFix/Model/Resolver/Products/DataProvider/CatalogCategoryProductJoinProcessor.php:

<?php

namespace Vendor\CategoryProductsSortFix\Model\Resolver\Products\DataProvider;

use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface;
use Magento\Framework\Api\SearchCriteriaInterface;

/**
 * Class CatalogCategoryProductJoinProcessor
 */
class CatalogCategoryProductJoinProcessor implements CollectionProcessorInterface
{
    /**
     * @param Collection $collection
     * @param SearchCriteriaInterface $searchCriteria
     * @param array $attributeNames
     * @return Collection
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function process(
        Collection $collection,
        SearchCriteriaInterface $searchCriteria,
        array $attributeNames
    ): Collection {
        if ($categoryFilter = $this->getFilterByFieldName($searchCriteria, 'category_id')) {
            try {
                $collection->joinField(
                    'position',
                    'catalog_category_product',
                    'position',
                    'product_id=entity_id',
                    '{{table}}.category_id IN (' . $categoryFilter->getValue() . ')',
                    'inner'
                );
            } catch (LocalizedException $e) {
                // join already exists
                return $collection;
            }
        }

        return $collection;
    }

    /**
     * @param SearchCriteriaInterface $searchCriteria
     * @param $fieldName
     * @return Filter|null
     */
    private function getFilterByFieldName(SearchCriteriaInterface $searchCriteria, $fieldName)
    {
        foreach ($searchCriteria->getFilterGroups() as $filterGroup) {
            /** @var Filter $filter */
            foreach ($filterGroup->getFilters() as $filter) {
                if ($filter->getField() === $fieldName) {
                    return $filter;
                }
            }
        }

        return null;
    }
}

in Vendor\CategoryProductsSortFix\Plugin\BeforeProductSearch.php:

<?php

namespace Vendor\CategoryProductsSortFix\Plugin;

use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\Api\SearchCriteriaInterface;

/**
 * Class BeforeProductSearch
 * @package Vendor\CategoryProductsSortFix\Plugin
 */
class BeforeProductSearch
{
    /**
     * @param \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch $productSearch
     * @param SearchCriteriaInterface $searchCriteria
     * @param SearchResultInterface $searchResult
     * @param array $attributes
     * @return array
     */
    public function beforeGetList(
        \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch $productSearch,
        SearchCriteriaInterface $searchCriteria,
        SearchResultInterface $searchResult,
        array $attributes = []
    ) {
        $filterGroup = $searchResult->getSearchCriteria()->getFilterGroups()[0];
        if ($filterGroup->getFilters()[0]->getField() == 'category_id') {
            $searchCriteria->setFilterGroups([$filterGroup]);
        }
        return [$searchCriteria, $searchResult, $attributes];
    }
}

Doing the following resolves the issue, but it isn’t a nice/good solution. This adds the category filterGroup to the searchCriteria, because position sort requires a join on the category index.

\Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ProductSearch::getList

/**
     * Get list of product data with full data set. Adds eav attributes to result set from passed in array
     *
     * @param SearchCriteriaInterface $searchCriteria
     * @param SearchResultInterface $searchResult
     * @param array $attributes
     * @return SearchResultsInterface
     */
    public function getList(
        SearchCriteriaInterface $searchCriteria,
        SearchResultInterface $searchResult,
        array $attributes = []
    ): SearchResultsInterface {

+       $filterGroup = $searchResult->getSearchCriteria()->getFilterGroups()[0];
+       $searchCriteria->setFilterGroups([$filterGroup]);

        /** @var Collection $collection */
        $collection = $this->collectionFactory->create();

        //Join search results
        $this->getSearchResultsApplier($searchResult, $collection, $this->getSortOrderArray($searchCriteria))->apply();

        $this->collectionPreProcessor->process($collection, $searchCriteria, $attributes);
        $collection->load();
        $this->collectionPostProcessor->process($collection, $attributes);

        $searchResults = $this->searchResultsFactory->create();
        $searchResults->setSearchCriteria($searchCriteria);
        $searchResults->setItems($collection->getItems());
        $searchResults->setTotalCount($searchResult->getTotalCount());
        return $searchResults;
    }