Выборка элементов инфоблока

Список элементов инфоблока получают через ORM-класс инфоблока Element{ApiCode}Table. Система создает класс по символьному коду API инфоблока, без этого кода класса нет. Класс строит запрос методом query() или принимает параметры массивом в методе getList(), фильтрует по полям элемента, значениям свойств и разделам, сортирует и разбивает результат на страницы.

Классический метод CIBlockElement::GetList() продолжает работать. Замены его фильтров и параметров собраны в разделе Заменить CIBlockElement::GetList(). Классический метод нужен только там, где у ORM нет замены, например для выборки с проверкой прав пользователя.

Перед выборкой подключите модуль iblock и получите ORM-класс элементов способом из раздела Получить элемент. Примеры используют переменную $elementNewsClass с ORM-классом инфоблока новостей и его свойства:

  • AUTHOR — Автор, строка,

  • KEYWORDS — Ключевые слова, множественная строка,

  • SOURCE — Источник, список.

Полный набор свойств инфоблока новостей приведен в статье Значения свойств элементов инфоблока. Получение одного элемента по ID или коду описано в статье Работа с элементами инфоблока.

Построить запрос

ORM-класс принимает параметры запроса двумя способами. Оба способа строят ORM-запрос и возвращают результат в одном формате.

Способ Как задать параметры Когда использовать
$elementNewsClass::query() Цепочкой методов setSelect(), where(), setOrder(), setLimit() Условия собираются в коде по шагам, нужны логика ИЛИ и подзапросы
$elementNewsClass::getList() Массивом с ключами select, filter, order, limit, offset Параметры приходят готовым массивом, например из настроек

Результат запроса возвращают методы:

  • fetchCollection() — коллекцию объектов элементов, пустую коллекцию, если элементов нет,

  • fetchObject() — первый объект или null,

  • fetchAll() — массив строк,

  • fetch() — следующую строку или false.

Значения свойств возвращает метод get() объекта элемента. Свойство попадает в объект, только если его код указан в setSelect(). Чтение свойств разных типов описано в разделе Прочитать значения.

В массиве строки от fetch() и fetchAll() значение свойства приходит под длинным служебным ключом вида IBLOCK_ELEMENTS_ELEMENT_<APICODE>_AUTHOR_VALUE, где <APICODE> — символьный код API инфоблока. Чтобы получить короткий ключ, задайте псевдоним в setSelect(), например 'AUTHOR_VALUE' => 'AUTHOR.VALUE'.

Если нужны только идентификаторы, например чтобы проверить, есть ли подходящие элементы, выбирайте ['ID'].

Код свойства в запросе должен быть в карте ORM-класса. В карту попадают только свойства с заполненным символьным кодом. Если кода нет в карте, запрос выбросит исключение Bitrix\Main\SystemException с текстом «Unknown field definition». Проверить код заранее можно методом $elementNewsClass::getEntity()->hasField('AUTHOR'), подробнее — в разделе Как свойства попадают в карту.

Класс Element{ApiCode}Table работает с одним инфоблоком. Чтобы выбрать базовые поля элементов нескольких инфоблоков, используйте общий класс Bitrix\Iblock\ElementTable с условием whereIn('IBLOCK_ID', $iblockIds). Свойств в этом классе нет, запрос со свойством выбросит исключение.

Пример. Получите десять последних новостей с флагом активности и автором. Для публичного списка добавьте условия из раздела Учесть, что отбирает ORM-запрос.

$newsList = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME', 'ACTIVE_FROM', 'AUTHOR'])
    ->where('ACTIVE', 'Y')
    ->setOrder(['ACTIVE_FROM' => 'DESC', 'ID' => 'DESC'])
    ->setLimit(10)
    ->fetchCollection()
;

foreach ($newsList as $news)
{
    $name = $news->getName();
    $author = $news->get('AUTHOR')?->getValue();
}

ORM-класс сам ограничивает запрос своим инфоблоком. Период активности, права и документооборот запрос не учитывает.

Общие правила построения запросов описаны в статье Построитель запросов.

Передать параметры массивом в getList

При переносе классического кода начните с метода getList(), потому что фильтр в нем тоже задается массивом. Метод принимает параметры запроса одним массивом:

  • select — поля и свойства в результате,

  • filter — условия отбора,

  • order — порядок сортировки,

  • limit и offset — число строк и смещение.

Оператор условия пишется перед кодом поля в ключе filter:

  • = — равенство, != — неравенство,

  • >, >=, <, <= — сравнение,

  • @ — значение из списка, !@ — значение не из списка,

  • >< — значение в диапазоне, значение ключа — массив из двух границ,

  • % — подстрока в любом месте значения.

Ключ без оператора для строкового поля ищет совпадение по шаблону LIKE, а не точное равенство. Указывайте оператор =, если нужно точное значение. Подробнее — в разделе Избегать LIKE в фильтрах.

Условия через ИЛИ задает вложенный массив с ключом 'LOGIC' => 'OR', как в классическом фильтре. Например, фильтр [['LOGIC' => 'OR', ['=AUTHOR.VALUE' => 'Редакция'], ['>=SORT' => 500]]] отбирает элементы автора «Редакция» или с сортировкой от 500.

Пример. Получите десять активных элементов с источником $sourceEnumId и текстом варианта списка.

$newsList = $elementNewsClass::getList([
    'select' => ['ID', 'NAME', 'SOURCE.ITEM'], // ITEM — вариант списка, без него getItem() вернет null
    'filter' => [
        '=ACTIVE' => 'Y',
        '=SOURCE.VALUE' => $sourceEnumId,
    ],
    'order' => ['ACTIVE_FROM' => 'DESC', 'ID' => 'DESC'],
    'limit' => 10,
])
    ->fetchCollection()
;

foreach ($newsList as $news)
{
    $sourceName = $news->get('SOURCE')?->getItem()?->getValue();
}

Переменная $sourceEnumId содержит ID варианта списка, найденный способом из раздела Найти вариант списка. Ключ SOURCE.ITEM в select присоединяет вариант списка, без него метод getItem() вернет null.

Не смешивайте параметры ORM-метода getList() и классического метода CIBlockElement::GetList(). Названия методов похожи, но формат параметров разный.

Условия по полям, свойствам и разделам ниже показаны в методах query(). В getList() те же условия задают ключами массива, например '=AUTHOR.VALUE', '@SECTIONS.ID' или '=SOURCE.ITEM.XML_ID'.

У метода getList() нет параметра, который убирает повторы строк. Если условие относится к множественному свойству или к списку разделов, например '@SECTIONS.ID', стройте запрос методом query() и добавляйте setDistinct(), подробнее — в разделе Отфильтровать по множественному свойству.

Отфильтровать элементы

Условия запроса задают методы where() и его варианты. Условия, заданные подряд, объединяются через И.

Задать условие по полю

Метод where() принимает код поля, оператор и значение. Если оператор не передан, метод проверяет равенство. Для типовых условий есть отдельные методы:

  • where('SORT', '>=', 100) — сравнение с операторами =, <>, >, >=, <, <=,

  • whereNot('ACTIVE', 'Y') — отрицание условия,

  • whereIn('ID', $ids) — значение из списка $ids,

  • whereBetween('SORT', 100, 500) — значение в диапазоне включительно,

  • whereNull('ACTIVE_TO') и whereNotNull('ACTIVE_TO') — пустое и непустое значение,

  • whereLike('NAME', '%обнов%') — совпадение с шаблоном, где % заменяет любые символы.

Метод whereIn() с пустым массивом не добавляет условие, и запрос вернет все элементы. Если список значений собирается в коде, проверьте, что он не пустой, до вызова whereIn().

Пример. Отберите активные элементы с сортировкой от 100 до 500, в названии которых есть слово «обнов».

$newsList = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME'])
    ->where('ACTIVE', 'Y')
    ->whereBetween('SORT', 100, 500)
    ->whereLike('NAME', '%обнов%')
    ->fetchCollection()
;

Поле активности ACTIVE хранит Y или N. Даты сравнивайте с объектом Bitrix\Main\Type\DateTime, пример приведен в разделе Учесть, что отбирает ORM-запрос.

Объединить условия через ИЛИ

Чтобы выполнилось хотя бы одно из условий, соберите их в объект фильтра Bitrix\Main\ORM\Query\Query::filter() с логикой or и передайте объект в where().

Пример. Отберите элементы автора «Редакция» или элементы с сортировкой от 500.

use Bitrix\Main\ORM\Query\Query;

$newsList = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME'])
    ->where(Query::filter()
        ->logic('or')
        ->where('AUTHOR.VALUE', 'Редакция')
        ->where('SORT', '>=', 500)
    )
    ->fetchCollection()
;

Объект фильтра принимает те же методы where(), whereIn(), whereNull() и другие, что и запрос. Фильтры можно вкладывать друг в друга.

Отфильтровать по свойству

Свойство в ORM — связанный объект с полями VALUE и DESCRIPTION. Чтобы задать условие по значению свойства, укажите поле в формате <код>.VALUE.

Тип свойства Условие Что передать
Строка, число where('AUTHOR.VALUE', 'Редакция') Значение свойства
Список, по ID варианта where('SOURCE.VALUE', $sourceEnumId) ID варианта, найденный способом из раздела Найти вариант списка
Список, по внешнему коду варианта where('SOURCE.ITEM.XML_ID', 'press') Внешний код XML_ID варианта. Связь ITEM присоединяет таблицу вариантов
Список, по тексту варианта where('SOURCE.ITEM.VALUE', 'Партнер') Текст варианта
Пустое значение whereNull('SOURCE.VALUE') Ничего. Условие отбирает элементы, у которых свойство не заполнено
Заполненное значение whereNotNull('SOURCE.VALUE') Ничего. Условие отбирает элементы, у которых свойство заполнено

Условия работают одинаково для обеих версий хранения свойств.

У свойства типа Привязка к элементам есть связь ELEMENT с полями связанного элемента, если у связанного инфоблока задан символьный код API. Например, для свойства-привязки с кодом RELATED, которого нет в инфоблоке новостей из примеров, условие where('RELATED.ELEMENT.CODE', 'security-update') отбирает элементы, у которых свойство ссылается на элемент с этим символьным кодом. Поля связанного элемента выбирают так же, например setSelect(['ID', 'RELATED.ELEMENT.NAME']). Объект связанного элемента возвращает вызов get('RELATED')->getElement().

Пример. Отберите элементы, у которых источник — вариант с внешним кодом press.

$newsList = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME'])
    ->where('SOURCE.ITEM.XML_ID', 'press')
    ->fetchCollection()
;

Отфильтровать по множественному свойству

Множественное свойство хранит несколько значений одного элемента. Условие по такому свойству присоединяет к элементу каждое подходящее значение, поэтому элемент повторяется в результате столько раз, сколько значений подошло. Коллекция объединяет повторы в один объект, но ограничение setLimit() и подсчет считают строки, а не элементы.

Добавьте setDistinct(), чтобы запрос вернул каждый элемент один раз.

Пример. Отберите первые 20 элементов, у которых в ключевых словах есть «обновление» или «безопасность».

$newsList = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME'])
    ->whereIn('KEYWORDS.VALUE', ['обновление', 'безопасность'])
    ->setDistinct()
    ->setOrder(['ID' => 'ASC'])
    ->setLimit(20)
    ->fetchCollection()
;

Без setDistinct() элемент с обоими словами займет две строки из двадцати, и коллекция вернет меньше двадцати элементов. Подсчет таких элементов описан в разделе Посчитать элементы. Общие правила выборки по связям «один ко многим» приведены в разделе «Выборки в отношениях 1:N и N:M» статьи Выборка данных.

Отфильтровать по разделу

Элемент может находиться в нескольких разделах. Поле IBLOCK_SECTION_ID хранит один раздел — основной, подробнее — в разделе Элементы. Связь SECTIONS хранит все разделы элемента.

Задача Условие
Элементы, у которых основной раздел — $sectionId where('IBLOCK_SECTION_ID', $sectionId)
Элементы, привязанные к разделу $sectionId where('SECTIONS.ID', $sectionId)
Элементы раздела по символьному коду раздела where('SECTIONS.CODE', 'events')
Элементы раздела и всех его подразделов Условия по границам раздела SECTIONS.LEFT_MARGIN и SECTIONS.RIGHT_MARGIN, пример ниже
Элементы вне разделов, в корне инфоблока where('IN_SECTIONS', 'N'). Элемент, записанный через ORM без setInSections(true), тоже попадет в результат, подробнее — в разделе Если элемент записывает ORM
Элементы только активных разделов с учетом активности родителей where('SECTIONS.GLOBAL_ACTIVE', 'Y') и setDistinct()

Условие по связи SECTIONS повторяет элемент для каждого подходящего раздела. Повтор возможен, только если под условие подходит больше одного раздела элемента. Так бывает, когда передан список разделов, отбирается поддерево или условие относится к полю раздела, например GLOBAL_ACTIVE. Тогда добавьте setDistinct(). Условие по одному ID раздела элемент не повторяет.

Раздел и его подразделы занимают в дереве непрерывный диапазон границ LEFT_MARGIN и RIGHT_MARGIN. Чтобы отобрать элементы всего поддерева, получите границы раздела из ORM-класса Bitrix\Iblock\SectionTable и отберите элементы разделов внутри этих границ.

Пример. Отберите элементы раздела и его подразделов. Переменная $sectionId содержит ID раздела.

use Bitrix\Iblock\SectionTable;

$section = SectionTable::getByPrimary($sectionId, [
    'select' => ['LEFT_MARGIN', 'RIGHT_MARGIN'],
])
    ->fetch()
;

if (!$section)
{
    throw new \RuntimeException('Раздел не найден');
}

$newsList = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME'])
    ->where('SECTIONS.LEFT_MARGIN', '>=', $section['LEFT_MARGIN'])
    ->where('SECTIONS.RIGHT_MARGIN', '<=', $section['RIGHT_MARGIN'])
    ->setDistinct()
    ->fetchCollection()
;

Запрос вернет те же элементы, что и классический фильтр SECTION_ID с INCLUDE_SUBSECTIONS Y. Активность разделов запрос не проверяет. Чтобы исключить элементы неактивных разделов, добавьте условие where('SECTIONS.GLOBAL_ACTIVE', 'Y').

Использовать подзапрос

Метод whereIn() принимает не только массив, но и другой ORM-запрос. Подзапрос заменяет классический фильтр CIBlockElement::SubQuery().

Пример. Отберите элементы, ID которых возвращает запрос по автору.

$authorQuery = $elementNewsClass::query()
    ->setSelect(['ID'])
    ->where('AUTHOR.VALUE', 'Редакция')
;

$newsList = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME'])
    ->whereIn('ID', $authorQuery)
    ->fetchCollection()
;

Подзапрос выбирает одно поле, значения которого сравнивает внешний запрос.

Отсортировать элементы

Метод setOrder() принимает массив, где ключ — код поля, а значение — направление ASC или DESC. Чтобы отсортировать по свойству, укажите поле <код>.VALUE.

Пример. Отсортируйте элементы по автору и по ID.

$newsList = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME'])
    ->setOrder([
        'AUTHOR.VALUE' => 'ASC',
        'ID' => 'ASC',
    ])
    ->fetchCollection()
;

Последним ключом указывайте поле с уникальными значениями, например ID. Без него элементы с одинаковым значением сортировки могут менять порядок между запросами, и при постраничной навигации элемент попадет на две страницы или пропадет.

Сортировка по множественному свойству повторяет элемент для каждого значения. Сортируйте по одиночным свойствам и полям элемента.

Чтобы отсортировать по полю основного раздела, укажите поле через связь IBLOCK_SECTION, например 'IBLOCK_SECTION.NAME' => 'ASC'. Элементы без раздела получат пустое значение и окажутся в начале списка при сортировке ASC.

Ограничить выборку

Метод setLimit() задает число строк результата, метод setOffset() — число строк, которые нужно пропустить. Если в setSelect() и в условиях нет множественных свойств и связи SECTIONS, одна строка соответствует одному элементу.

Получить множественные свойства для ограниченной выборки

Множественное свойство в setSelect() возвращает строку на каждое значение. С setLimit(10) такой запрос вернет десять значений, а не десять элементов. Элементы с несколькими значениями займут несколько строк, и часть элементов в результат не попадет.

Получите элементы в два шага:

  1. Выберите элементы без множественных свойств с нужным setLimit().

  2. Загрузите множественные свойства для полученной коллекции методом fill().

Пример. Получите десять элементов и их ключевые слова.

$newsList = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME'])
    ->setOrder(['ID' => 'DESC'])
    ->setLimit(10)
    ->fetchCollection()
;

$newsList->fill(['KEYWORDS']);

foreach ($newsList as $news)
{
    $keywords = [];

    foreach ($news->get('KEYWORDS') ?? [] as $keyword)
    {
        $keywords[] = $keyword->getValue();
    }
}

Метод fill() выполняет один дополнительный запрос для всей коллекции. Другой способ загрузить свойства — запрос с условием whereIn('ID', ...), он описан в разделе Прочитать свойства многих элементов.

Разбить список на страницы

Объект Bitrix\Main\UI\PageNavigation хранит номер и размер страницы и вычисляет смещение. Передайте его значения в setOffset() и setLimit(), а общее число элементов запросите методом countTotal(true).

Пример. Получите страницу списка из 20 элементов. Номер страницы объект навигации берет из адреса запроса.

use Bitrix\Main\UI\PageNavigation;

$navigation = new PageNavigation('news-page');
$navigation->setPageSize(20);
$navigation->initFromUri();

$result = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME'])
    ->where('ACTIVE', 'Y')
    ->setOrder(['SORT' => 'ASC', 'ID' => 'ASC'])
    ->setOffset($navigation->getOffset())
    ->setLimit($navigation->getLimit())
    ->countTotal(true)
    ->exec()
;

$navigation->setRecordCount($result->getCount());
$newsList = $result->fetchCollection();

Метод initFromUri() читает номер страницы из параметра news-page адреса в формате ?news-page=page-2, а в режиме ЧПУ, то есть человекопонятных адресов, — из части пути /news-page/page-2/. Значение без префикса, например ?news-page=2, номер страницы не задает. Метод getCount() результата возвращает общее число строк без учета setLimit().

Если вызвать initFromUri() до setRecordCount(), как в примере, номер страницы больше последней не ограничивается, и запрос вернет пустой список.

Чтобы объект навигации сам заменил такой номер на последнюю страницу, вызовите методы в другом порядке:

  1. Посчитайте элементы методом queryCountTotal() или способом из раздела Посчитать элементы.

  2. Если элементов нет, не запрашивайте страницу. При нулевом числе элементов объект навигации возвращает отрицательное смещение, и запрос завершится ошибкой SQL.

  3. Передайте число в setRecordCount() и только затем вызовите initFromUri().

  4. Выполните запрос страницы с setOffset() и setLimit() без countTotal(true).

Ссылки на страницы выводит компонент bitrix:main.pagenavigation, которому передают объект навигации. Настройка компонента и навигация без подсчета описаны в статье Постраничная навигация.

Если условие запроса может повторить элемент — по множественному свойству или по нескольким разделам, — общее число нужно считать отдельно способом из раздела Посчитать элементы.

Посчитать элементы

Методы подсчета возвращают число строк, которые подходят под условия:

  • $elementNewsClass::getCount($filter) — принимает фильтр в формате getList(),

  • queryCountTotal() — считает строки построенного запроса,

  • countTotal(true) и getCount() результата — считают строки вместе с выборкой.

Если условие может повторить элемент — по множественному свойству или по нескольким разделам, — каждый элемент считается столько раз, сколько у него подходящих значений. Метод setDistinct() на подсчет не влияет. Чтобы посчитать элементы, отберите их ID подзапросом и посчитайте строки внешнего запроса.

Пример. Посчитайте элементы, у которых в ключевых словах есть «обновление» или «безопасность».

$matchingIds = $elementNewsClass::query()
    ->setSelect(['ID'])
    ->whereIn('KEYWORDS.VALUE', ['обновление', 'безопасность'])
;

$count = (int)$elementNewsClass::query()
    ->whereIn('ID', $matchingIds)
    ->queryCountTotal()
;

Метод queryCountTotal() возвращает число строкой, поэтому пример приводит его к целому.

Сгруппировать элементы

Чтобы посчитать элементы по значениям поля или свойства, например по вариантам списка, сгруппируйте строки методом setGroup() и добавьте вычисляемое поле с функцией COUNT. Вычисляемое поле описывает объект Bitrix\Main\ORM\Fields\ExpressionField, его регистрирует метод registerRuntimeField().

Пример. Посчитайте элементы для каждого варианта списка Источник.

use Bitrix\Main\ORM\Fields\ExpressionField;

$rows = $elementNewsClass::query()
    ->setSelect(['SOURCE_ID' => 'SOURCE.VALUE', 'ELEMENT_COUNT'])
    ->registerRuntimeField(new ExpressionField('ELEMENT_COUNT', 'COUNT(%s)', 'ID'))
    ->setGroup(['SOURCE.VALUE'])
    ->fetchAll()
;

Каждая строка содержит ID варианта в ключе SOURCE_ID и число элементов в ключе ELEMENT_COUNT. Элементы без значения свойства попадают в строку с SOURCE_ID, равным null. Вычисляемые поля описаны в статье Построитель запросов.

Обработать много элементов

Для импорта, экспорта и агентов обходите элементы пакетами по возрастанию ID, а не через растущий setOffset(). Условие по ID использует первичный ключ, а границы пакетов не сдвигаются, если во время обхода другой процесс удаляет элементы. Метод fetch() возвращает строки по одной и не собирает весь пакет в памяти объектами.

Пример. Обработайте все элементы инфоблока пакетами по 500.

$batchSize = 500;
$lastId = 0;

do
{
    $result = $elementNewsClass::query()
        ->setSelect(['ID', 'NAME'])
        ->where('ID', '>', $lastId)
        ->setOrder(['ID' => 'ASC'])
        ->setLimit($batchSize)
        ->exec()
    ;

    $rowCount = 0;

    while ($row = $result->fetch())
    {
        $rowCount++;
        $lastId = (int)$row['ID'];
        // Обработайте элемент
    }
}
while ($rowCount === $batchSize);

Цикл заканчивается, когда пакет содержит меньше $batchSize строк. Сохраняйте $lastId во внешнем состоянии задания, если обработку нужно продолжить после сбоя.

Не добавляйте в такой запрос множественные свойства. Их значения загрузите для пакета отдельно способом из раздела Получить множественные свойства для ограниченной выборки.

Кешировать запрос

ORM-запрос можно закешировать методом setCacheTtl(), который задает время жизни кеша в секундах. Свойства присоединяются к запросу как связанные таблицы, поэтому добавьте cacheJoins(true). Без него запрос со связями не кешируется. Общие правила описаны в разделе «Кеширование выборки» статьи Выборка данных.

Кеш ORM-запроса сбрасывается, когда методы CIBlockElement::Add(), Update() и Delete() меняют элемент. Если изменились только значения свойств, например через CIBlockElement::SetPropertyValuesEx(), кешированный запрос продолжит возвращать прежние значения до окончания времени жизни кеша. Не кешируйте запросы, которые должны сразу показывать изменения свойств, или задавайте короткое время жизни.

Пример. Закешируйте список активных новостей с автором на час.

$rows = $elementNewsClass::query()
    ->setSelect(['ID', 'NAME', 'AUTHOR_VALUE' => 'AUTHOR.VALUE'])
    ->where('ACTIVE', 'Y')
    ->setOrder(['ID' => 'DESC'])
    ->setLimit(10)
    ->setCacheTtl(3600)
    ->cacheJoins(true)
    ->fetchAll()
;

Получить записи справочника для списка

Свойство типа Справочник хранит внешний код UF_XML_ID записи Highload-блока. Чтобы вывести название записи для списка элементов, соберите значения свойства всех элементов и запросите записи Highload-блока одним запросом. Порядок действий и пример описаны в разделе «Получить данные справочника» статьи Связи и справочники на Highload-блоках.

Заменить CIBlockElement::GetList()

Таблицы помогают перенести существующий код с классического метода CIBlockElement::GetList() на ORM-класс.

Заменить фильтр

Фильтр CIBlockElement::GetList() Условие ORM-запроса Что учесть
'IBLOCK_ID' => $iblockId Условие не нужно ORM-класс Element{ApiCode}Table сам ограничивает запрос своим инфоблоком
'IBLOCK_ID' => [$id1, $id2] Bitrix\Iblock\ElementTable и whereIn('IBLOCK_ID', [$id1, $id2]) Общий класс выбирает только поля элементов, без свойств
'=CODE' => $code where('CODE', $code) —
'NAME' => $name, 'CODE' => $code и другие строковые поля без оператора whereLike('NAME', $name) Классический ключ без оператора для строки ищет по шаблону LIKE. Для точного значения используйте where()
'ID' => $id where('ID', $id) —
'%NAME' => $text whereLike('NAME', '%' . $text . '%') Классический фильтр экранирует символы % и _ в $text, а whereLike() — нет, в ORM они работают как шаблон
Вложенный массив ['LOGIC' => 'OR', [...], [...]] where(Query::filter()->logic('or')->where(...)->where(...)) Пример приведен в разделе Объединить условия через ИЛИ. В getList() вложенный массив с 'LOGIC' => 'OR' работает так же, как в классическом фильтре
'!ID' => $id, '!=ID' => $id whereNot('ID', $id) —
'>SORT' => 100, '>=SORT', '<SORT', '<=SORT' where('SORT', '>', 100) и другие операторы —
'><SORT' => [100, 500] whereBetween('SORT', 100, 500) Границы входят в диапазон
'ID' => $ids whereIn('ID', $ids) Проверьте, что массив $ids не пустой. Пустой whereIn() не добавляет условие, и запрос вернет все элементы
'PROPERTY_AUTHOR' => $value where('AUTHOR.VALUE', $value) Классический ключ без оператора для строкового свойства ищет по шаблону LIKE, where() — точное значение
'PROPERTY_KEYWORDS' => $value или массив значений для множественного свойства where('KEYWORDS.VALUE', $value) или whereIn('KEYWORDS.VALUE', $values) и setDistinct() Без setDistinct() элемент повторяется для каждого подходящего значения, подробнее — в разделе Отфильтровать по множественному свойству. Пустой массив в whereIn() снимает условие
'PROPERTY_SOURCE' => $enumId where('SOURCE.VALUE', $enumId) —
'PROPERTY_SOURCE_VALUE' => $text where('SOURCE.ITEM.VALUE', $text) Классический ключ без оператора ищет текст варианта по шаблону LIKE, where() — точное значение
'PROPERTY_SOURCE' => false whereNull('SOURCE.VALUE') —
'!PROPERTY_SOURCE' => false whereNotNull('SOURCE.VALUE') —
'SECTION_ID' => $sectionId where('SECTIONS.ID', $sectionId) Для списка разделов используйте whereIn('SECTIONS.ID', $sectionIds) с setDistinct() и проверьте, что массив $sectionIds не пустой
'SECTION_ID' => $sectionId и 'INCLUDE_SUBSECTIONS' => 'Y' Условия по SECTIONS.LEFT_MARGIN и SECTIONS.RIGHT_MARGIN Пример приведен в разделе Отфильтровать по разделу
'SECTION_ID' => 0 where('IN_SECTIONS', 'N') —
'SECTION_CODE' => $code where('SECTIONS.CODE', $code) —
'SECTION_GLOBAL_ACTIVE' => 'Y' where('SECTIONS.GLOBAL_ACTIVE', 'Y') Добавьте setDistinct()
'PROPERTY_RELATED.CODE' => $code для привязки к элементам where('RELATED.ELEMENT.CODE', $code) У связанного инфоблока должен быть задан символьный код API
'ACTIVE' => 'Y' where('ACTIVE', 'Y') —
'ACTIVE_DATE' => 'Y' Условия по ACTIVE_FROM и ACTIVE_TO Пример приведен в разделе Учесть, что отбирает ORM-запрос
'ID' => CIBlockElement::SubQuery(...) whereIn('ID', $subQuery) Пример приведен в разделе Использовать подзапрос
'!PROPERTY_KEYWORDS' => $value для множественного свойства whereNotIn('ID', $subQuery), где подзапрос отбирает ID элементов с where('KEYWORDS.VALUE', $value) Условие whereNot('KEYWORDS.VALUE', $value) отберет элементы, у которых есть другое значение, и пропустит элементы без значений
SHOW_HISTORY не задан — классический метод скрывает копии документооборота и неопубликованные элементы where('WF_STATUS_ID', 1) и whereNull('WF_PARENT_ELEMENT_ID') Нужно, если в инфоблоке включен документооборот. ORM-запрос без этих условий вернет копии
'CHECK_PERMISSIONS' => 'Y' Нет замены ORM-класс не проверяет права. Если список должен учитывать права пользователя на отдельные элементы, используйте классический метод. Проверка права для инфоблока или раздела описана в разделе Проверить права пользователя

Заменить остальные параметры

Параметр CIBlockElement::GetList() Замена в ORM Что учесть
$arOrder с ключом PROPERTY_<код> setOrder(['<код>.VALUE' => 'ASC']) Сортировка по множественному свойству повторяет элемент для каждого значения. Сортируйте по одиночным свойствам
$arGroupBy — пустой массив для подсчета queryCountTotal() или getCount() Классический метод считает элементы, ORM — строки. Для условий по множественному свойству считайте способом из раздела Посчитать элементы
$arGroupBy с полями или PROPERTY_<код> setGroup() и вычисляемое поле с COUNT Пример приведен в разделе Сгруппировать элементы
$arNavStartParams с nPageSize и iNumPage Объект PageNavigation, setOffset(), setLimit() и countTotal(true) Пример приведен в разделе Разбить список на страницы
$arNavStartParams с nTopCount setLimit() Метод setLimit() считает строки, и множественное свойство в выборке дает строку на каждое значение. Классический nTopCount ведет себя так же только в инфоблоках, которые хранят свойства в общей таблице
$arSelectFields с PROPERTY_<код> setSelect(['<код>']) и get('<код>') у объекта Значения свойства возвращает объект значения, подробнее — в разделе Прочитать значения. Для массива строк задайте псевдоним, например 'AUTHOR_VALUE' => 'AUTHOR.VALUE'. Множественное свойство при setLimit() загрузите методом fill(), подробнее — в разделе Получить множественные свойства для ограниченной выборки
DETAIL_PAGE_URL в $arSelectFields Нет метода ORM Адрес детальной страницы по шаблону инфоблока формируют методы GetNext() и GetNextElement() классического результата. Для строки ORM-запроса адрес по шаблону возвращает классический метод CIBlock::ReplaceDetailUrl(), пример приведен в разделе Показать другие новости источника

Цикл while ($row = $result->GetNext()) замените перебором коллекции или вызовами fetch(). Метод GetNext() экранирует значения для HTML, а ORM возвращает исходные значения, поэтому экранируйте их при выводе. Замены методов результата GetList() — GetNext(), GetNextElement(), GetFields() и GetProperties() — приведены в разделе Заменить методы классического API для чтения.

Проверить результат

Перед публикацией кода проверьте выборку на тестовом инфоблоке:

  1. Создайте элементы с разными значениями одиночных и множественных свойств, в нескольких разделах и в корне инфоблока.

  2. Выполните запрос и сравните ID элементов с ожидаемым набором.

  3. Если условие относится к множественному свойству или к связи SECTIONS, проверьте, что элемент с несколькими подходящими значениями встречается один раз и не уменьшает размер страницы.

  4. Сравните общее число элементов с числом, которое показывает административный раздел для тех же условий.

  5. Пройдите по всем страницам списка и убедитесь, что каждый элемент встречается один раз.

Способы ускорить выборку и выбрать метод получения данных собраны в статье Производительность и частые ошибки.