При фильтрации по множественным свойствам возникает важный вопрос: как именно должны работать условия?
- Логика «ИЛИ» (OR): Найти элементы, у которых свойство имеет хотя бы одно из указанных значений.
- Логика «И» (AND): Найти элементы, у которых свойство имеет все указанные значения одновременно.
Логика «ИЛИ» — простой случай
Это поведение по умолчанию как в старом API, так и в D7 ORM. Достаточно передать массив значений в фильтр.
Пример (старое API CIBlockElement::GetList):
Найти товары, у которых цвет «Красный» ИЛИ «Синий».
$arFilter = [
"IBLOCK_ID" => 10,
"ACTIVE" => "Y",
"PROPERTY_COLOR" => ["Красный", "Синий"] // Просто передаем массив
];
$rsElements = CIBlockElement::GetList([], $arFilter);
// Пример (D7 ORM `ElementTable::getList`):
$result = ElementTable::getList([
'filter' => [
'=IBLOCK_ID' => 10,
'=ACTIVE' => 'Y',
'=PROPERTY_COLOR.VALUE' => ["Красный", "Синий"] // Тоже просто массив
]
]);
Логика «И» — сложный случай
Это нетривиальная задача, так как в базе данных значения множественных свойств хранятся в разных строках. Просто передать массив здесь не получится. Решение заключается в использовании подзапросов.
Задача: Найти товары, у которых цвет и «Красный», и «Синий» одновременно.
Решение для старого API (CIBlockElement::GetList)
Нужно создать вложенный фильтр, который будет проверять наличие каждого значения отдельно и считать совпадения.
$colors = ["Красный", "Синий"];
$colorsCount = count($colors);
$arFilter = [
"IBLOCK_ID" => 10,
"ACTIVE" => "Y",
// Создаем подфильтр
[
"LOGIC" => "AND",
"ID" => CIBlockElement::SubQuery("ID", [
"IBLOCK_ID" => 10,
"PROPERTY_COLOR" => $colors, // Находим все элементы, у которых есть хотя бы один из цветов
"CNT" => $colorsCount // Группируем по ID и считаем кол-во совпадений
]),
],
];
// Указываем группировку для SubQuery
$arGroupBy = ["ID"];
$rsElements = CIBlockElement::GetList([], $arFilter, $arGroupBy);
Как это работает:
- CIBlockElement::SubQuery создает подзапрос.
- Внутри подзапроса мы ищем все элементы, у которых свойство COLOR равно либо «Красный», либо «Синий».
- Ключевой момент: мы группируем результат по ID элемента и добавляем CNT => $colorsCount (в нашем случае CNT => 2). Это означает, что в результат подзапроса попадут только те ID элементов, у которых нашлось ровно 2 совпадения по цветам.
- Основной запрос выбирает элементы, ID которых вернул подзапрос.
Решение для D7 ORM
В D7 ORM это делается элегантнее с помощью runtime-полей и подзапросов Query.
use Bitrix\Iblock\ElementTable;
use Bitrix\Main\Entity\Query;
use Bitrix\Main\Entity\ExpressionField;
$colors = ["Красный", "Синий"];
$colorsCount = count($colors);
// Создаем подзапрос
$subQuery = new Query(ElementTable::getEntity());
$subQuery
->setSelect(['IBLOCK_ELEMENT_ID'])
->where('IBLOCK_ID', 10)
->whereIn('PROPERTY_COLOR.VALUE', $colors)
->addGroup('IBLOCK_ELEMENT_ID')
->having('CNT', '=', $colorsCount)
->registerRuntimeField(
new ExpressionField('CNT', 'COUNT(DISTINCT %s)', 'PROPERTY_COLOR.VALUE')
);
// Основной запрос
$result = ElementTable::getList([
'select' => ['ID', 'NAME'],
'filter' => [
'=IBLOCK_ID' => 10,
'@ID' => $subQuery // Используем подзапрос
]
]);
Как это работает:
Логика та же, но выражена более явно:
- Мы создаем объект Query для подзапроса.
- Выбираем ID элементов, фильтруем по нужным цветам (whereIn).
- Группируем по IBLOCK_ELEMENT_ID.
- Добавляем runtime-поле CNT для подсчета уникальных совпавших цветов.
- Используем having (аналог WHERE для сгруппированных данных), чтобы оставить только те элементы, у которых CNT равен количеству искомых цветов.
- В основном запросе в filter мы используем оператор @ (аналог SQL IN), передавая ему наш готовый подзапрос.
Вывод:
Фильтрация по множественным свойствам с логикой «ИЛИ» проста и интуитивна. Для логики «И» требуется использовать более сложную конструкцию с подзапросом и группировкой.
D7 ORM, хотя и выглядит многословнее в этом случае, предоставляет более гибкий и читаемый синтаксис для построения таких сложных запросов по сравнению с «магическим» CNT в старом API.
множественное свойство, фильтр ‘И’, CIBlockElement::GetList, D7 ORM, подзапрос, фильтрация Битрикс, логика ‘И’, логика ‘ИЛИ’.