Перейти к содержанию
Инструкция

Фильтрация элементов по полю «Привязка к элементам» в Битрикс

Как отфильтровать элементы инфоблока по множественному свойству типа «Привязка к элементам» (E). Разбираем прямой фильтр PROPERTY_CODE, связку через второй инфоблок в два шага и подзапрос CIBlockElement::SubQuery, который убирает лишний цикл на PHP.

Обновлено 22 июля 7 минут чтения

Свойство типа «Привязка к элементам» (в 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».

Частые вопросы

Как в фильтре GetList обратиться к свойству «Привязка к элементам»?

По ключу PROPERTY_, например PROPERTY_CITY. В значение можно передать одиночный ID или массив ID – во втором случае сработает условие «любой из перечисленных».

Как отфильтровать через два инфоблока одним запросом?

Вместо цикла со сбором ID передайте в фильтр объект CIBlockElement::SubQuery('ID', array(...)): он превращается во вложенный SQL, и связанные ID вычисляет сама база. Один запрос вместо «выборка + цикл + второй запрос».

Когда лучше собирать ID циклом, а когда использовать SubQuery?

SubQuery – когда сами ID не нужны и важна скорость. Два шага с циклом – когда собранный список ID пригодится ещё где-то на странице или логику отбора трудно уложить в один фильтр.

Почему выборка по множественному свойству возвращает дубли?

Множественное свойство join'ится, и элемент попадает в результат по числу своих значений. Чтобы получить уникальные элементы, группируйте по ID или собирайте результат в массив с ключом-идентификатором.

Фильтр по дате DATE_ACTIVE_FROM возвращает пусто – в чём причина?

Чаще всего дату подают в неверном формате. В фильтр нужно передавать дату в формате сайта – его как раз возвращает ConvertTimeStamp(false, 'SHORT'), – а не Unix-timestamp.

Валентина Меланина

Хотите обсудить свой проект?

Помогу с разработкой, аналитикой и AI-видимостью вашего сайта

Если у вас есть задача — от внедрения разметки и аналитики до полной переработки сайта — напишите, обсудим объём и подход.