Не так давно появилась такая замечательная возможность API, как использование подзапросов при выборе элементов ИБ. Как все знают, она позволяет в одном запросе к БД выстроить ещё более сложную логику - вложенную выборку из другого связанного ИБ.
Примечание: применимо только к полю ID элемента основного запроса.
В документации приведен пример
Выбрать авторов написавших книги в 21-ом веке
По коду очевидно, что есть два инфоблока "Авторы" и "Книги". У "Книг" есть привязка к "Авторам".
А как быть, если необходимо получить не авторов, по параметрам книг, а книги, по параметрам авторов (со сложной логикой)?.. [spoiler]
Например, задача:
Есть инфоблоки "Книги", "Авторы", "Страны".
У книг есть привязка к авторам, у авторов - к странам.
Нужно одним запросом (имеется в виду CIBlockElement::GetList()) выбрать все книги, имя автора которых начинается на букву "К", а страна (место рождения автора) - на букву "И".
Конечно, задача синтетическая и скорее придумана именно для этого поста, нежели взята из реальной жизни. На больших проектах задачи обычно более сложные и способы фильтрации не просто по имени, а со сложной логикой (И/ИЛИ), множеством условий для каждой сущности и т.д.
Оговорюсь сразу: стандартными средствами этого сделать нельзя, и вот здесь тоже есть одно "НО", позволяющее всё-таки решить задачу "малой кровью".
Первая мысль:
Зная, что есть такая штука как подзапросы, пытаемся сделать выборку по инфоблоку Книги и фильтром по имени автора
Всё работает, гуд. Следующий шаг (фильтровать по имени страны, к которой привязан автор) ставит в тупик - сделать так не получится, потому как конструкции вида
"PROPERTY_AUTHOR.PROPERTY_COUNTRY.NAME" => "И%"
работать не будут...
Вторая мысль:
Использовать подзапросы, сконструировать что-то типа
Но тут мы упираемся в то самое примечание, которое есть в документации: мы не можем использовать PROPERTY_AUTHOR, только ID. При этом, если всё-таки вписать 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
которая даёт пищу для размышления и небольшую подсказку в какую сторону копать...
Решение:
Путем непродолжительных изучений вышеуказанного файла находим в файле
Тем самым мы обходим примечание из документации и теперь можем строить "матрешки", теоретически, любой вложенности, например, для решения поставленной в самом начале поста задачи будет работать вот такой код:
Озвученный подход внедрен на нескольких проектах (да-да, мы знаем, что модифицировать ядро нельзя) и пока проблем не наблюдается. Нагрузку решение выдерживает, "подводных камней" не обнаружено, возможностей фильтрации добавилось, мягко говоря, очень много
Примечание:
Нашелся только один баг фича): Если у ИБ "Книги" свойство "Авторы" множественное, то будет дублирование выбранных книг (по каждому элементу столько раз, сколько нашлось совпадений по значению множественного свойства).
------------ а по моему уже вечность прошла И в том числе с момента упоминания однобоксти подзапросов.
Пока ничего нового не изобрели, ваш пост будет как решение, но... но смущает "кирдык" ядра Может правильнее написать свой прямой простой запрос, но Битрикс оставить живым и здоровым? Ну или как то классы попробовать переопределить? или уж свой метод написать?
Суть поста - не просто рассказать что мы изобрели очередной велосипед (да ещё и влезли в ядро), а показать в большей степени разработчикам из Битрикса, что такая полезная фича реализуется относительно простым способом (~10 строк кода). Может комплексно протестируют и внедрят.
написать свой прямой простой запрос
Это точно нет: во-первых, у нас на то есть API, чтобы не писать прямые запросы в БД. Во-вторых, не такой уж и простой он получится.
Ну или как то классы попробовать переопределить
Это можно, но тогда придется переопределять весь класс CIBlockElement, что может привести к такой же плачевной ситуации после глобального обновления, что и в нашем примере.
Короче ИМХО это всё это костыли, а в статье изложено реальное решение.
Данный пост - иллюстрация того, что очень "малой кровью" можно сильно расширить API механизма SubQuery. На готовое решение это, конечно, не претендует, но пища для размышления разработчикам дана - пользоваться (хоть и с оговорками) можно.
Что касается модуля DEFA.Tools:
1. Он действительно популярный. В нем есть какое-то количество функционала, применяемое разными людьми в разных задачах 2. Он БЕСПЛАТНЫЙ, что подразумевает использование as-is и означает, что как такового отдельного разработчика под него нету. А следовательно и нет выделенного бюджета на развитие.
Я не говорю, что мы просто забросили его как есть. Нет - мы оказываем техподдержку (когда люди пишут нам через форма на сайте), правим какие-то баги и более того - зная, что не весь функционал уже работает как надо (платформа то меняется, нужно актуализировать и само решение), выделили время разработчиков на его доработку/переработку.
То есть в скором времени мы протестируем и выпустим большое обновление, которое поправит все явные (и не очень) баги.
О, знакомая тематика:) Писал подобное пару лет назад, только пошел по пути допила а-ля
"PROPERTY_AUTHOR.PROPERTY_COUNTRY.NAME" => "И%"
http://dev.1c-bitrix.ru/community/web.../blog/sku/ Тоже создавал идею И подобных предложений/запросов на данный функционал там и на форуме довольно много. Жаль, что битрикс очень медленно развивает ядро, которое постепенно морально устаревает.
Хак, описанный выше, не сработает с версии модуля Информационные блоки 16.5.6. Кроме описанных в блоге действий, придется в коде класса CIblockElement добавлять (возвращать) функцию _sql_in, менять GetList и PrepareGetList. В общем, дело это неблагодарное и невеселое (сама в шоке что сработало). Предлагаю настаивать на выпуск официального релиза с возможностью использовать подзапросы для свойств, а не ломать чужие велосипеды((
Ирина Анатольевна Дороган написал: Хак, описанный выше, не сработает с версии модуля Информационные блоки 16.5.6. Кроме описанных в блоге действий, придется в коде класса CIblockElement добавлять (возвращать) функцию _sql_in, менять GetList и PrepareGetList. В общем, дело это неблагодарное и невеселое (сама в шоке что сработало)
вот так, похоже, работает. достаточно "малой кровью"
Ирина Анатольевна Дороган написал: Предлагаю настаивать на выпуск официального релиза с возможностью использовать подзапросы для свойств, а не ломать чужие велосипеды((
это да... но идее уже почти 4 года, неплохой рейтинг и даже статус "на голосовании", но когда сделают - не понятно https://idea.1c-bitrix.ru/8080/
При этом UserStatusHistoryTable у меня из подключения к posgresql с соответствующим коннектором для него. В результате чего получается, что сабквери компилится в синтаксисе posgresql, а внешний квери на mysql. вот возвращаемый результат:
SEL ECT
`tmp_lz4_si9u_xfux`.`MAX` AS `MAX`
FR OM (SEL ECT
MAX("bizteck_ams_orm_desktop_user_user_status_history"."ID") AS "MAX"
FR OM "ams_user_status_history" "bizteck_ams_orm_desktop_user_user_status_history"
WHERE "bizteck_ams_orm_desktop_user_user_status_history"."USER_ID" = 8 AND "bizteck_ams_orm_desktop_user_user_status_history"."DATE" = '2023-01-25') `tmp_lz4_si9u_xfux`
Опять же, может я что-то не так сделал, но внешняя выборка получается из tmp_lz4_si9u_xfux, а нужна-то по факту выборка из bizteck_ams_orm_desktop_user_user_status_history (хотя если в select добавить не MAX а поля таблицы, то может получится выборка из bizteck_ams_orm_desktop_user_user_status_history.... надо проверять методом научного тыка)
Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».