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.