Свойство типа «Привязка к элементам» (в API – тип E) хранит связь одного элемента инфоблока с элементами другого инфоблока: город у курса, бренд у товара, автор у статьи. Рано или поздно возникает задача выбрать элементы по такой привязке – причём не по одному инфоблоку, а по цепочке из двух. Разберём приём на связке «Расписание → Курсы → Города» и покажем, как свести всё к одному SQL-запросу.
Что такое поле «Привязка к элементам»
Это свойство инфоблока, значением которого служит ID элемента другого (или того же) инфоблока. Оно бывает одиночным и множественным. В фильтрах CIBlockElement::GetList к нему обращаются по ключу PROPERTY_<КОД> – например, PROPERTY_CITY. В значение можно передать как одиночный ID, так и массив ID: во втором случае сработает условие «любой из перечисленных».
Задача: есть три инфоблока – «Расписание курсов» (ID = 6), «Курсы» (ID = 5) и «Города» (ID = 4). Нужно выбрать все занятия из расписания, у которых дата начала не раньше сегодняшней, а связанный курс проходит в Москве. Город привязан не к расписанию напрямую, а к курсу – поэтому одним «плоским» фильтром не обойтись.
Шаг 1. Собираем ID курсов нужного города
Первым запросом получаем идентификаторы курсов, у которых свойство «Город» (PROPERTY_CITY) указывает на нужный элемент справочника. Просим у выборки только поле ID – это важно для скорости: лишние поля и свойства инфоблок подтягивает отдельными запросами.
$IBlockSchedule = 6; // Расписание курсов
$IBlockCourses = 5; // Курсы
$intCityID = 318; // ID элемента «Москва» в инфоблоке городов
$arCoursesID = array();
$resCourseID = CIBlockElement::GetList(
array(),
array('IBLOCK_ID' => $IBlockCourses, 'ACTIVE' => 'Y', 'PROPERTY_CITY' => $intCityID),
false,
false,
array('ID')
);
while ($arCourseID = $resCourseID->GetNext(false, false)) {
$arCoursesID[] = $arCourseID['ID'];
}
Четвёртый аргумент false отключает постраничную навигацию, пятый (array('ID')) – список полей выборки. Аргументы GetNext(false, false) отключают HTML-обработку и перекодировку значений: она не нужна, когда забираем только числовой ID, и заметно экономит время на больших выборках.
Шаг 2. Фильтруем расписание по собранным ID
Теперь выбираем элементы «Расписания», у которых дата активности не раньше текущей, а свойство-привязка к курсу (PROPERTY_COURSE_ID) попадает в собранный список. Множественному свойству-привязке можно передать массив значений напрямую:
$arFilter = array(
'IBLOCK_ID' => $IBlockSchedule,
'ACTIVE' => 'Y',
'>=DATE_ACTIVE_FROM' => ConvertTimeStamp(false, 'SHORT'), // не раньше сегодняшней даты
'PROPERTY_COURSE_ID' => $arCoursesID,
);
Готовый $arFilter передают в компонент «Список новостей» (bitrix:news.list) через параметр FILTER_NAME, либо используют в собственном вызове CIBlockElement::GetList. Ключевой момент: перед DATE_ACTIVE_FROM стоит оператор >=, а ConvertTimeStamp(false, 'SHORT') возвращает текущую дату в формате сайта.
Как убрать цикл: один запрос через SubQuery
Шаг 1 с циклом по PHP можно убрать целиком – Битрикс умеет подставлять в фильтр вложенный подзапрос через CIBlockElement::SubQuery. Метод принимает имя поля и фильтр и возвращает объект, который база разворачивает в подзапрос. Тогда «курсы города Москва» вычисляются на стороне СУБД, а не в цикле PHP:
$arFilter = array(
'IBLOCK_ID' => $IBlockSchedule,
'ACTIVE' => 'Y',
'>=DATE_ACTIVE_FROM' => ConvertTimeStamp(false, 'SHORT'),
'PROPERTY_COURSE_ID' => CIBlockElement::SubQuery('ID', array(
'IBLOCK_ID' => $IBlockCourses,
'ACTIVE' => 'Y',
'PROPERTY_CITY' => $intCityID,
)),
);
Когда какой способ выбирать
- SubQuery – когда ID курсов сами по себе не нужны и важна скорость: один запрос вместо «выборка + цикл + второй запрос».
- Два шага – когда собранный список ID пригодится ещё где-то (например, для отдельного блока «города, где идёт курс») или когда логику отбора трудно уложить в один фильтр.
- Массив ID напрямую хорош для коротких списков; на сотнях значений конструкция
IN (1,2,3,…)раздувает SQL – тогда предпочтительнее SubQuery.
Типичные ошибки
- Кэш компонента. Если фильтр зависит от даты (
ConvertTimeStamp) или от параметра из адреса, добавьте эти значения в ключ кэша компонента черезADDITIONAL_CACHE_ID– иначе «вчерашняя» выборка застрянет в кэше. - Права доступа.
CIBlockElement::GetListпо умолчанию учитывает права текущего пользователя. Чтобы выбрать все элементы независимо от прав (в агенте, на cron), передавайте'CHECK_PERMISSIONS' => 'N'в фильтре. - Множественное свойство и дубли. Выборка по множественному свойству может вернуть один элемент несколько раз (по числу значений). Нужны уникальные элементы – группируйте по
IDили собирайте результат в массив с ключом-идентификатором. - Неверный формат даты. В фильтр по
DATE_ACTIVE_FROMподавайте дату в формате сайта (его и отдаётConvertTimeStamp), а не Unix-timestamp – иначе сравнение молча вернёт пустой результат.
Смежные приёмы работы с инфоблоками – в гайдах «Получить ID из GET и вывести элементы инфоблока», «Подсчёт общего количества элементов в инфоблоке» и «Как вывести определённое поле элемента инфоблока». Про фильтрацию уже выведенных элементов на стороне вёрстки – «Фильтрация с помощью псевдокласса :not».