Кастомные фильтры и сортировки списка элементов инфоблоков в GraphQL API
Артем Житник
Типовая ситуация - нужно получить список элементов инфоблока. Но этот список бесполезен, если его нельзя фильтровать и сортировать в рамках логики проекта. Пока писал модуль завис на этом вопросе. Если решать в лоб, можно передать через API параметр содержащий объект фильтров и сортировок, который на бекенде транслировать в соответствующие параметры CIblockElement::GetList. Мне показалось неоправданным давать такие широкие возможности пользователю API.
Кстати, CIblockElement::GetList пришлось использовать из-за того что в нем можно выполнять сложные условия, в частности по свойствам, каталогу и т.д. Изначально я хотел обойтись простым ElementTable.
Как реализовать функционал фильтрации? А что если дать возможность владельцу сайта самому определять что фильтровать, какими параметрами API при этом пользоваться? Менять код модуля не вариант, остается реализовать событие, что я и сделал.
Пример, нужно добавить фильтрацию по активности и тегу, а также сортировку по времени активности. В /local/php_interface/init.php добавляем код:
use Bitrix\Main\EventManager; use Bitrix\Main\Event; use GraphQL\Type\Definition\InputObjectType; use Rbx\GraphQL\Type\{Iblock, Main}; EventManager::getInstance()->addEventHandler("rbx.graphql", "OnAddQueryFilter", [MyClass::class, "addCustomFilters"]); class MyClass { public static function addCustomFilters(Event $event) { $params = $event->getParameters(); if ($params["QUERY_TYPE_CLASS"] != Iblock\IblockType::class) { return; } return [ "INPUT_ARGS" => [ "filters" => new InputObjectType([ "name" => "FilterInput", "fields" => [ "active" => [ "type" => Main\Types::boolean(), "description" => "Активность" ], "tag" => [ "type" => Main\Types::string(), "description" => "Тег" ], ] ]), "orders" => new InputObjectType([ "name" => "OrderInput", "fields" => [ "byActiveFrom" => [ "type" => Main\Types::string(), "description" => "Сортировать по времени активности" ], ] ]), ], "GETLIST_FILTERS" => function (array $args): array { $filter = []; if (isset($args["filters"])) { if (isset($args["filters"]["active"])) { $filter += [ "=ACTIVE" => $args["filters"]["active"] ? "Y" : "N", ]; } if (isset($args["filters"]["tag"])) { $filter += [ "%TAGS" => $args["filters"]["tag"], ]; } } return $filter; }, "GETLIST_ORDERS" => function (array $args): array { $orders = []; if (isset($args["orders"])) { $byActiveFrom = $args["orders"]["byActiveFrom"] ?? null; if (in_array($byActiveFrom, ["ASC", "DESC"])) { $orders += [ "ACTIVE_FROM" => $byActiveFrom, ]; } } return $orders; } ]; } }
Здесь мы обрабатываем событие OnAddQueryFilter. Определяем входящие параметры API INPUT_ARGS, а также используем заданные пользователем значения этих параметров в подготовке параметров для CIblockElement::GetList.
Для запроса к API:
{ iblock { blog { getElementList(filters: {active: true}, orders: {byActiveFrom: "ASC"}) { id activeFrom active } } } }
Получим ответ:
{ "data": { "iblock": { "blog": { "getElementList": [ { "id": "33", "activeFrom": "2021-09-29T12:23:00+09:00", "active": true }, { "id": "35", "activeFrom": "2021-09-30T15:10:12+09:00", "active": true }, { "id": "36", "activeFrom": "2021-10-02T19:55:22+09:00", "active": true } ] } } } }
Таким образом можно реализовать любую логику необходимую в рамках проекта.