Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Offering Filtering and Sorting in the GraphQL Query

Offering Filtering and Sorting in the GraphQL Query

~8 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

The events query from chapter 13 currently always returns every active event in insertion order. This chapter adds a filter and a sort parameter, both as custom input types (chapter 6), deliberately reusing the existing core building blocks FilterTypeInput and SortEnum.

Declaring the filter input type

app/code/Mironsoft/Event/etc/schema.graphqls
input EventFilterInput @doc(description: "Identifies which fields to filter events by") {
    title: FilterTypeInput
    location: FilterTypeInput
}

input EventSortInput @doc(description: "Specifies which fields to sort events by") {
    title: SortEnum
    start_at: SortEnum
}

FilterTypeInput is the same core type that already showed up in chapter 3 in product search - it brings operators such as eq, like, in along, without the events module having to redefine them. SortEnum is a plain enum with the values ASC and DESC, also from Magento_CatalogGraphQl.

Adding the arguments to the query

app/code/Mironsoft/Event/etc/schema.graphqls
type Query {
    events(
        filter: EventFilterInput
        sort: EventSortInput
        pageSize: Int = 20
        currentPage: Int = 1
    ): Events
        @resolver(class: "Mironsoft\\Event\\Model\\Resolver\\Events")
        @doc(description: "Returns a filtered, sorted, paginated list of active events")
}

Extending the DataProvider

The getList() signature from chapter 13 gets two additional, optional parameters. Both are translated into filters or sort orders through the already-existing SearchCriteriaBuilder:

app/code/Mironsoft/Event/Model/Resolver/DataProvider/Events.php (excerpt, extended getList signature)
/**
 * Fetches active events for the given page, optionally filtered and sorted.
 *
 * @param int $pageSize Number of events per page
 * @param int $currentPage Requested page, 1-based
 * @param array<string, array<string, string>>|null $filter Field => [operator => value]
 * @param array<string, string>|null $sort Field => direction (ASC/DESC)
 * @return array{items: array<int, array<string, mixed>>, total_count: int, page_info: array<string, int>}
 */
public function getList(
    int $pageSize,
    int $currentPage,
    ?array $filter = null,
    ?array $sort = null
): array {
    $this->searchCriteriaBuilder->addFilter(EventInterface::IS_ACTIVE, 1);

    foreach ($filter ?? [] as $field => $operatorValuePairs) {
        foreach ($operatorValuePairs as $operator => $value) {
            $this->searchCriteriaBuilder->addFilter($field, $value, $operator);
        }
    }

    $this->searchCriteriaBuilder->setCurrentPage($currentPage);
    $this->searchCriteriaBuilder->setPageSize($pageSize);
    $searchCriteria = $this->searchCriteriaBuilder->create();

    foreach ($sort ?? [] as $field => $direction) {
        $sortOrder = $this->sortOrderBuilder->setField($field)
            ->setDirection($direction)
            ->create();
        $searchCriteria->setSortOrders([$sortOrder]);
    }

    // ... rest as in chapter 13: call the repository, map items, build page_info
}

$sortOrderBuilder (\Magento\Framework\Api\SortOrderBuilder) gets added as another constructor dependency - consistently via constructor property promotion, like every other dependency in this project.

Adjusting the resolver

$filter = $args['filter'] ?? null;
$sort = $args['sort'] ?? null;

return $this->eventsDataProvider->getList(
    (int) ($args['pageSize'] ?? 20),
    (int) ($args['currentPage'] ?? 1),
    $filter,
    $sort
);

The extended query in practice

query {
  events(
    filter: { location: { eq: "Berlin" } }
    sort: { start_at: ASC }
    pageSize: 10
  ) {
    items { title location start_at }
    total_count
  }
}

Achtung: $args['filter'] arrives from GraphQL as a nested array with the operator as the key (['location' => ['eq' => 'Berlin']]) - almost identical to what addFieldToFilter() itself expects as its second parameter. This structural similarity is no accident: FilterTypeInput was deliberately modeled so it translates directly into Magento's collection filter API.

Tipp: When several filter fields are set at once, SearchCriteriaBuilder::addFilter() combines them with AND by default - OR combinations would need a shared filter group via addFilters() with multiple filters in the same array. For most storefront filters (like here), the simple AND combination is entirely sufficient.

Chapter 15 wraps up block 4: querying a single event by identifier, instead of always loading the entire (filtered) list.