Не так давно появилась такая замечательная возможность API, как использование подзапросов при выборе элементов ИБ.
Как все знают, она позволяет в одном запросе к БД выстроить ещё более сложную логику - вложенную выборку из другого связанного ИБ.
Есть даже документация по тому методу (), и как всегда есть одно "НО":
В документации приведен пример
По коду очевидно, что есть два инфоблока "Авторы" и "Книги". У "Книг" есть привязка к "Авторам".
А как быть, если необходимо получить не авторов, по параметрам книг, а книги, по параметрам авторов (со сложной логикой)?..
[spoiler]
Например, задача:
Есть инфоблоки "Книги", "Авторы", "Страны".
У книг есть привязка к авторам, у авторов - к странам.
Нужно одним запросом (имеется в виду CIBlockElement::GetList()) выбрать все книги, имя автора которых начинается на букву "К", а страна (место рождения автора) - на букву "И".
Конечно, задача синтетическая и скорее придумана именно для этого поста, нежели взята из реальной жизни.
На больших проектах задачи обычно более сложные и способы фильтрации не просто по имени, а со сложной логикой (И/ИЛИ), множеством условий для каждой сущности и т.д.
Оговорюсь сразу: стандартными средствами этого сделать нельзя, и вот здесь тоже есть одно "НО", позволяющее всё-таки решить задачу "малой кровью".
Первая мысль:
Зная, что есть такая штука как подзапросы, пытаемся сделать выборку по инфоблоку Книги и фильтром по имени автора
Всё работает, гуд. Следующий шаг (фильтровать по имени страны, к которой привязан автор) ставит в тупик - сделать так не получится, потому как конструкции вида
работать не будут...
Вторая мысль:
Использовать подзапросы, сконструировать что-то типа
Но тут мы упираемся в то самое примечание, которое есть в документации: мы не можем использовать PROPERTY_AUTHOR, только ID.
При этом, если всё-таки вписать PROPERTY_AUTHOR, то возникает ошибка
которая даёт пищу для размышления и небольшую подсказку в какую сторону копать...
Решение:
Путем непродолжительных изучений вышеуказанного файла находим в файле
строку 1455, куда нужно вставить некий "хак" вида
Тем самым мы обходим примечание из документации и теперь можем строить "матрешки", теоретически, любой вложенности, например, для решения поставленной в самом начале поста задачи будет работать вот такой код:
Представьте какая получается длина SQL-запроса =)
Все необходимые файлы приложены. Кому интересно - можно изучить.
Вот ещё скриншот результат работы скрипта subquery_example.php -
В боевых условиях:
Озвученный подход внедрен на нескольких проектах (да-да, мы знаем, что модифицировать ядро нельзя) и пока проблем не наблюдается.
Нагрузку решение выдерживает, "подводных камней" не обнаружено, возможностей фильтрации добавилось, мягко говоря, очень много
Примечание:
Нашелся только одинбаг фича): Если у ИБ "Книги" свойство "Авторы" множественное, то будет дублирование выбранных книг (по каждому элементу столько раз, сколько нашлось совпадений по значению множественного свойства).
Идея:
Создал идею , голосуйте
Как все знают, она позволяет в одном запросе к БД выстроить ещё более сложную логику - вложенную выборку из другого связанного ИБ.
Есть даже документация по тому методу (), и как всегда есть одно "НО":
Примечание: применимо только к полю ID элемента основного запроса. |
Выбрать авторов написавших книги в 21-ом веке |
А как быть, если необходимо получить не авторов, по параметрам книг, а книги, по параметрам авторов (со сложной логикой)?..
[spoiler]
Например, задача:
Есть инфоблоки "Книги", "Авторы", "Страны".
У книг есть привязка к авторам, у авторов - к странам.
Нужно одним запросом (имеется в виду CIBlockElement::GetList()) выбрать все книги, имя автора которых начинается на букву "К", а страна (место рождения автора) - на букву "И".
Конечно, задача синтетическая и скорее придумана именно для этого поста, нежели взята из реальной жизни.
На больших проектах задачи обычно более сложные и способы фильтрации не просто по имени, а со сложной логикой (И/ИЛИ), множеством условий для каждой сущности и т.д.
Оговорюсь сразу: стандартными средствами этого сделать нельзя, и вот здесь тоже есть одно "НО", позволяющее всё-таки решить задачу "малой кровью".
Первая мысль:
Зная, что есть такая штука как подзапросы, пытаемся сделать выборку по инфоблоку Книги и фильтром по имени автора
array( "IBLOCK_ID" => $IBLOCK_BOOKS, "ACTIVE" => "Y", "PROPERTY_AUTHOR.NAME" => "К%" ) |
"PROPERTY_AUTHOR.PROPERTY_COUNTRY.NAME" => "И%" |
Вторая мысль:
Использовать подзапросы, сконструировать что-то типа
array( "IBLOCK_ID" => $IBLOCK_BOOKS, "ACTIVE" => "Y", "PROPERTY_AUTHOR" => CIBlockElement::SubQuery( "ID", array( "IBLOCK_ID" => $IBLOCK_AUTHORS, "ACTIVE" => "Y", "NAME" => "К%" ) ) ) |
При этом, если всё-таки вписать PROPERTY_AUTHOR, то возникает ошибка
Warning: mb_strlen() expects parameter 1 to be string, object given in /var/www/html/bitrix/modules/iblock/classes/general/iblock.php on line 2234 |
Решение:
Путем непродолжительных изучений вышеуказанного файла находим в файле
/var/www/html/bitrix/modules/iblock/classes/general/iblockelement.php |
if(is_object($propVAL))
{
if($db_prop["PROPERTY_TYPE"]=="G" || $db_prop["PROPERTY_TYPE"]=="E")
{
if($db_prop["VERSION"]==2 && $db_prop["MULTIPLE"]=="N")
$fn = "FPS".$iPropCnt.".PROPERTY_".$db_prop["ORIG_ID"];
else
$fn = "FPV".$iPropCnt.".VALUE";
$r = $propVAL->_sql_in($fn, $cOperationType);
}
}
else
{
if(!is_array($propVAL))
$propVAL = array($propVAL);
...
$r = CIBlock::FilterCreateEx("FPV".$iPropCnt.".VALUE", $propVAL, "string", $bFullJoin, $cOperationType);
}
}
} |
$rsItems = CIBlockElement::GetList(
array(),
array(
"IBLOCK_ID" => $IBLOCK_BOOKS,
"ACTIVE" => "Y",
"PROPERTY_AUTHOR" => CIBlockElement::SubQuery(
"ID",
array(
"IBLOCK_ID" => $IBLOCK_AUTHORS,
"ACTIVE" => "Y",
"NAME" => "К%",
"PROPERTY_COUNTRY" => CIBlockElement::SubQuery(
"ID",
array(
"IBLOCK_ID" => $IBLOCK_COUNTRIES,
"ACTIVE" => "Y",
"NAME" => "И%",
)
),
)
),
),
false,
false,
array("ID", "NAME", "PROPERTY_AUTHOR.ID", "PROPERTY_AUTHOR.NAME", "PROPERTY_AUTHOR.PROPERTY_COUNTRY")
);
while($arItem = $rsItems->Fetch())
{
print_r($arItem);
}
|
Все необходимые файлы приложены. Кому интересно - можно изучить.
Вот ещё скриншот результат работы скрипта subquery_example.php -
В боевых условиях:
Озвученный подход внедрен на нескольких проектах (да-да, мы знаем, что модифицировать ядро нельзя) и пока проблем не наблюдается.
Нагрузку решение выдерживает, "подводных камней" не обнаружено, возможностей фильтрации добавилось, мягко говоря, очень много

Примечание:
Нашелся только один
Идея:
Создал идею , голосуйте