1С Битрикс · 7 мин чтения

CIBlockElement::GetList: выборка элементов инфоблока без ошибок

CIBlockElement::GetList - метод, с которым сталкивается любой, кто дорабатывает каталог на 1С-Битрикс: вывод товаров на витрину, фильтр по свойствам, экспорт в CRM или подготовка данных для доставки через СДЭК. Метод старый, живёт с версии D6, но именно поэтому вокруг него скопилось больше всего рабочих привычек, которые ломаются на конкретном проекте - то фильтр не срабатывает, то сортировка по свойству кладёт сервер на 60 000 товаров, то count теряется из-за неверных параметров навигации. Ниже - рабочие паттерны, которые я использую на реальных каталогах, а не теория из документации.

Как устроен CIBlockElement::GetList в 1С-Битрикс

Сигнатура метода такая:

CIBlockElement::GetList(
    $arOrder,          // сортировка
    $arFilter,         // фильтр выборки
    $arGroupBy,        // группировка, обычно false
    $arNavStartParams, // постраничная навигация
    $arSelectFields    // какие поля и свойства вернуть
);

Важный момент, который часто упускают: метод возвращает объект CDBResult, а не массив. Чтобы получить и поля, и свойства элемента одним проходом, нужен новый вызов GetNextElement(), а не старый GetNext(). Старый метод тянет только поля и подтягивает свойства отдельными запросами на каждый элемент - на выборке в 500 позиций это легко превращается в 500 лишних SQL-запросов.

$res = CIBlockElement::GetList(
    array("SORT" => "ASC"),
    array("IBLOCK_ID" => 14, "ACTIVE" => "Y"),
    false,
    array("nPageSize" => 20, "iNumPage" => 1),
    array("ID", "NAME", "PROPERTY_COLOR", "PROPERTY_SIZE")
);

while ($ob = $res->GetNextElement()) {
    $arFields = $ob->GetFields();
    $arProps  = $ob->GetProperties();
}

Если свойства не нужны - GetNext() быстрее и проще, но тогда в выборке останутся только поля из таблицы b_iblock_element, свойства придётся тянуть отдельно через CIBlockElement::GetProperty.

Фильтр GetList: типичные ошибки, из-за которых выборка ломается

Самая частая причина “пустой” выборки - забытый IBLOCK_ID. Без него метод ищет по всем инфоблокам сайта, и если совпадений по остальным условиям нет, результат оказывается пустым даже при формально правильном фильтре. Второй по частоте баг - путаница между PROPERTY_XXX и PROPERTY_XXX_VALUE: первый ищет по значению свойства через справочник (для списочных свойств это ID варианта), второй - по фактическому текстовому значению.

  • ACTIVE - фильтрует по статусу элемента, но не проверяет даты активности
  • ACTIVE_DATE - отдельный флаг “Y”, который включает проверку ACTIVE_FROM / ACTIVE_TO относительно текущей даты
  • >PROPERTY_PRICE и подобные префиксы (>, <, >=, <=, !) - работают, но операторы нужно ставить перед ключом, а не после
  • CHECK_PERMISSIONS - по умолчанию “Y”, метод проверяет права на каждый элемент; на фоновых скриптах и в cron это можно смело отключать (“N”), выборка ускоряется заметно

Пример рабочего фильтра для витрины с товарами в наличии и в диапазоне цен:

$arFilter = array(
    "IBLOCK_ID" => 14,
    "ACTIVE" => "Y",
    "ACTIVE_DATE" => "Y",
    "CHECK_PERMISSIONS" => "N",
    ">CATALOG_AVAILABLE" => 0,
    "PROPERTY_COLOR_VALUE" => "Красный",
);

На проекте с каталогом крепежа (около 60 000 товаров) именно связка “забыли CHECK_PERMISSIONS + фильтр по трём свойствам сразу” превращала загрузку каталожной страницы в 4-5 секунд. После переноса части логики в кэшируемый компонент и отключения проверки прав для фонового экспорта время упало до 300-400 мс.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Сортировка arOrder: почему порядок влияет на скорость выборки

Сортировка по полям элемента (SORT, ID, NAME, ACTIVE_FROM) работает быстро, потому что это обычные колонки таблицы b_iblock_element с индексами. Сортировка по свойству - это уже JOIN с таблицей b_iblock_element_property (или её сплит-версией для больших инфоблоков), и без индекса по значению свойства запрос легко перерастает в файловую сортировку на стороне MySQL.

Способ сортировки Где хранится значение Время на 50 000 товаров
SORT (поле элемента) b_iblock_element ~40 мс
PROPERTY_PRICE (свойство) JOIN с b_iblock_element_property ~900‑1200 мс без индекса
Та же сортировка, но с кэшем компонента файловый/Redis кэш ~5-10 мс при повторном вызове

Если сортировка по цене или другому часто используемому свойству нужна регулярно, я обычно выношу это значение в отдельное поле каталога (например, CATALOG_PRICE_1 в таблице цен) - там уже есть нормальные индексы, и сортировка идёт без JOIN по EAV-таблице. Второй вариант - держать инфоблок небольшим, а тяжёлые фильтрованные списки собирать через highloadblock или отдельную таблицу под конкретную задачу.

Выбор полей и свойств: arSelectFields и SELECT_PROPS

Пятый параметр GetList стоит заполнять всегда - без него метод тянет все поля и все свойства элемента, даже если реально нужны три значения. На карточке товара с 40 свойствами это ощутимая переплата по памяти и времени, особенно если элементов в выборке сотни.

$arSelect = array("ID", "NAME", "DETAIL_PAGE_URL", "PROPERTY_WEIGHT", "PROPERTY_DIMENSIONS");

$res = CIBlockElement::GetList(
    array(),
    array("IBLOCK_ID" => 14, "ID" => array(101, 102, 103)),
    false,
    false,
    $arSelect
);

Этот же принцип я использую при подготовке данных для расчёта доставки: если нужно отдать в СДЭК вес и габариты товара, нет смысла тянуть весь набор свойств карточки - выбираются только PROPERTY_WEIGHT и PROPERTY_DIMENSIONS, запрос на 200 позиций заказа отрабатывает почти мгновенно.

Если свойство множественное (например, несколько изображений или несколько вариантов цвета), GetProperties() вернёт его как массив VALUE - это стоит учитывать при формировании JSON для фронтенда или API, иначе легко получить ошибку при попытке напрямую вывести значение как строку.

Постраничная навигация и подсчёт количества элементов

Четвёртый параметр отвечает за пагинацию. Два рабочих варианта:

  • nPageSize + iNumPage - классическая постраничная навигация с автоматическим подсчётом общего числа страниц
  • nTopCount - просто ограничивает количество строк без расчёта постраничности, полезно для “топ-10” блоков и виджетов

Чтобы получить только количество элементов без загрузки самих записей (типичная задача - счётчик “найдено товаров: N” в фильтре каталога), не нужно гонять полную выборку дважды. Работает связка nTopCount => 0 с последующим вызовом SelectedRowsCount():

$arNavParams = array("nTopCount" => 0);

$res = CIBlockElement::GetList(
    array(),
    $arFilter,
    false,
    $arNavParams,
    array("ID")
);

$count = $res->SelectedRowsCount();

Ошибка, которую вижу регулярно в чужом коде: сначала делают полный GetList без навигации, гоняют весь while-цикл, чтобы посчитать количество через инкремент счётчика, и только потом делают отдельный запрос с nPageSize для реальной выборки. Это два полных прохода по таблице там, где хватает одного лёгкого запроса на count и одного на данные.

Когда GetList тормозит: оптимизация и альтернативы

На каталогах до 5-10 тысяч элементов классический GetList с грамотным фильтром обычно не создаёт проблем. На инфоблоках от 30-40 тысяч позиций и выше, особенно с фильтрацией по нескольким свойствам одновременно, EAV-модель Битрикса начинает давить на MySQL - каждое дополнительное свойство в фильтре это ещё один JOIN.

Что реально помогает на практике:

  • Кэширование через CPHPCache или встроенный кэш компонента - если данные не меняются каждую секунду, повторный вызов из кэша быстрее любой оптимизации SQL
  • Вынос часто фильтруемых свойств в отдельные индексированные поля (как цена в таблице каталога)
  • Переход на BitrixIblockElementTable (D7 ORM) для новых модулей - она даёт нормальный query builder и не тянет автоматически лишние поля
  • Для сложного полнотекстового поиска и фасетного фильтра по десяткам свойств - вынос индекса в Elasticsearch или Sphinx, GetList в таком случае используется только для получения полных данных по уже отфильтрованным ID

Если каталог на Битриксе разросся до состояния, когда стандартные способы фильтрации и сортировки уже не спасают, и переделка индексов, кэшей и структуры инфоблоков требует полноценного аудита - это как раз тот случай, когда быстрее отдать задачу на сторону. Можно посмотреть мои услуги по доработке и оптимизации сайтов на 1С-Битрикс - обычно за один аудит удаётся понять, где именно теряется время: в фильтре, в сортировке по свойствам или в отсутствии кэша на уровне компонента.

Чтобы сайт работал без сбоев

Техподдержка

от 15 000 ₽/мес

Подробнее →

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

Чем CIBlockElement::GetList отличается от новой ORM BitrixIblockElementTable?

GetList - метод старого ядра D6, который живёт в Битриксе с середины 2000‑х и до сих пор поддерживается разработчиком. ElementTable - часть ORM D7, появившейся позже: она даёт query builder, ленивую загрузку связей и более предсказуемую работу с памятью. На старых проектах я обычно оставляю GetList там, где логика уже написана и работает стабильно, а новые модули и API пишу через ElementTable - переписывать рабочий легаси без причины смысла нет.

Почему GetList возвращает пустой результат, хотя элементы точно есть в базе?

В 80% случаев причина в трёх местах: не указан или указан неверно IBLOCK_ID, фильтр ACTIVE_DATE => “Y” отсекает элементы с истёкшей или ещё не наступившей датой активности, либо перепутаны ключи PROPERTY_XXX и PROPERTY_XXX_VALUE для списочного свойства. Проверяю в таком порядке: сначала убираю все условия кроме IBLOCK_ID, смотрю, приходят ли элементы вообще, потом добавляю фильтры по одному.

Как получить только активные элементы с учётом дат ACTIVE_FROM и ACTIVE_TO?

Одного ACTIVE => “Y” недостаточно - это поле не проверяет даты. Нужно добавить “ACTIVE_DATE” => “Y”, тогда метод сам сравнит текущую дату с полями ACTIVE_FROM и ACTIVE_TO элемента и исключит из выборки то, что ещё не опубликовано или уже снято с публикации по расписанию.

Как ускорить выборку, если фильтр идёт сразу по нескольким свойствам инфоблока?

Каждое свойство в фильтре - это дополнительный JOIN с EAV-таблицей, и с ростом числа свойств запрос растёт нелинейно. Если фильтр по одному-двум свойствам используется часто (цена, наличие, бренд), выносите их в отдельные индексированные поля вместо стандартных свойств инфоблока. Для витрин с десятками фильтруемых характеристик разумнее строить отдельный поисковый индекс и обращаться к GetList только для дозагрузки полных данных по уже найденным ID.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Самозанятый Калинкин Н. А. · работаю с физлицами и юрлицами

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно