Кастомные фильтры и сортировки списка элементов инфоблоков в 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
}
]
}
}
}
}
Таким образом можно реализовать любую логику необходимую в рамках проекта.