Если вы всё ещё пишете CIBlockElement::GetList — пора остановиться. В Битрикс давно есть D7 ORM и Iblock API 2.0, которые работают быстрее, читаются лучше и поддерживают автокомплит в IDE. Разбираем переход со старого API на новый с реальными примерами.
Старый API vs Новый: разница в подходе
Старый подход — процедурный, с массивами параметров, которые нигде не документированы (кроме головы разработчика):
// Старый API — CIBlockElement::GetList
$rsElements = CIBlockElement::GetList(
['SORT' => 'ASC'],
[
'IBLOCK_ID' => 5,
'ACTIVE' => 'Y',
'SECTION_ID' => 10,
],
false,
['nPageSize' => 20],
['ID', 'NAME', 'DETAIL_PAGE_URL', 'PROPERTY_PRICE', 'PROPERTY_BRAND']
);
while ($arElement = $rsElements->GetNext()) {
echo $arElement['NAME'] . ' — ' . $arElement['PROPERTY_PRICE_VALUE'];
}
Новый подход — ORM с чейнингом, типизированными фильтрами и поддержкой IDE:
// Новый API — Iblock\Elements
use Bitrix\Iblock\Iblock;
$elements = Iblock::wakeUp(5)
->getEntityDataClass()::getList([
'select' => ['ID', 'NAME', 'PRICE_' => 'PRICE', 'BRAND_' => 'BRAND.ELEMENT'],
'filter' => [
'=ACTIVE' => 'Y',
'IBLOCK_SECTION_ID' => 10,
],
'order' => ['SORT' => 'ASC'],
'limit' => 20,
]);
foreach ($elements as $element) {
echo $element->get('NAME') . ' — ' . $element->get('PRICE_VALUE');
}
Настройка Инфоблоков 2.0
Чтобы использовать новый API, инфоблок должен работать в режиме 2.0 (хранение свойств в отдельной таблице). Проверяем и включаем:
- Админка → Контент → Инфоблоки → нужный тип → нужный инфоблок
- Вкладка «Поля» → «Хранение свойств значений» = В отдельных таблицах
- Указать API-код инфоблока (например,
catalog) — он будет использоваться в ORM
После этого Битрикс создаст таблицу b_iblock_element_prop_sNN для свойств и класс ORM.
Генерация ORM-классов
Для автокомплита в IDE генерируем аннотации:
// В init.php или консольном скрипте
use Bitrix\Iblock\Iblock;
$iblock = Iblock::wakeUp(5); // ID инфоблока
$entity = $iblock->getEntityDataClass();
// Имя класса для PhpStorm
echo get_class($entity); // Bitrix\Iblock\Elements\ElementCatalogTable
Для проектов с несколькими инфоблоками удобно создать хелпер:
// /local/php_interface/classes/IblockHelper.php
namespace App;
use Bitrix\Iblock\Iblock;
use Bitrix\Main\Loader;
class IblockHelper
{
private static array $cache = [];
public static function getEntity(int $iblockId): string
{
if (!isset(self::$cache[$iblockId])) {
Loader::includeModule('iblock');
self::$cache[$iblockId] = Iblock::wakeUp($iblockId)->getEntityDataClass();
}
return self::$cache[$iblockId];
}
}
// Использование
$catalogClass = \App\IblockHelper::getEntity(5);
$elements = $catalogClass::getList([...]);
Выборка данных — основные паттерны
Простая выборка с фильтром
use Bitrix\Iblock\Iblock;
$entity = Iblock::wakeUp(5)->getEntityDataClass();
$elements = $entity::getList([
'select' => ['ID', 'NAME', 'ACTIVE_FROM', 'DETAIL_PAGE_URL'],
'filter' => [
'=ACTIVE' => 'Y',
'>=ACTIVE_FROM' => new \Bitrix\Main\Type\DateTime('01.01.2025', 'd.m.Y'),
],
'order' => ['ACTIVE_FROM' => 'DESC'],
'limit' => 10,
'offset' => 0,
'count_total' => true,
]);
// Общее количество (для пагинации)
$total = $elements->getCount();
foreach ($elements as $element) {
echo $element->get('ID') . ': ' . $element->get('NAME');
}
Выборка со свойствами
$elements = $entity::getList([
'select' => [
'ID',
'NAME',
'PRICE_' => 'PRICE', // Свойство PRICE
'BRAND_' => 'BRAND', // Свойство BRAND
'COLOR_' => 'COLOR', // Множественное свойство
],
'filter' => [
'=ACTIVE' => 'Y',
'>=PRICE.VALUE' => 1000, // Фильтр по свойству
'=BRAND.VALUE' => 'Apple', // Точное совпадение
],
]);
foreach ($elements as $element) {
$price = $element->get('PRICE_VALUE');
$brand = $element->get('BRAND_VALUE');
echo "$brand — $price ₽";
}
Связь «Привязка к элементам»
Если свойство — привязка к элементам другого инфоблока:
$elements = $entity::getList([
'select' => [
'ID',
'NAME',
// Получаем данные связанного элемента
'BRAND_ID' => 'BRAND.ELEMENT.ID',
'BRAND_NAME' => 'BRAND.ELEMENT.NAME',
],
'filter' => ['=ACTIVE' => 'Y'],
]);
foreach ($elements as $element) {
echo $element->get('NAME') . ' (бренд: ' . $element->get('BRAND_NAME') . ')';
}
Операторы фильтрации
| Оператор | Описание | Пример |
|---|---|---|
= |
Равно | '=ACTIVE' => 'Y' |
!= |
Не равно | '!=STATUS' => 'ARCHIVE' |
> |
Больше | '>PRICE.VALUE' => 0 |
>= |
Больше или равно | '>=SORT' => 100 |
< |
Меньше | '<ACTIVE_FROM' => new DateTime() |
% |
LIKE (содержит) | '%NAME' => 'iPhone' |
= (массив) |
IN | '=ID' => [1, 2, 3] |
Логические операторы:
// AND — по умолчанию
'filter' => [
'=ACTIVE' => 'Y',
'>PRICE.VALUE' => 100,
]
// OR — через массив логики
'filter' => [
'LOGIC' => 'OR',
['=BRAND.VALUE' => 'Apple'],
['=BRAND.VALUE' => 'Samsung'],
]
Кеширование
D7 ORM поддерживает встроенное кеширование:
$elements = $entity::getList([
'select' => ['ID', 'NAME', 'PRICE_' => 'PRICE'],
'filter' => ['=ACTIVE' => 'Y'],
'cache' => [
'ttl' => 3600, // Время жизни кеша в секундах
'cache_joins' => true, // Кешировать JOIN-запросы
],
]);
Для ручного управления кешем:
use Bitrix\Main\Data\Cache;
$cache = Cache::createInstance();
if ($cache->initCache(3600, 'catalog_items_' . md5(serialize($filter)), '/catalog/')) {
$items = $cache->getVars()['items'];
} elseif ($cache->startDataCache()) {
$items = [];
$elements = $entity::getList([...]);
foreach ($elements as $el) {
$items[] = [
'ID' => $el->get('ID'),
'NAME' => $el->get('NAME'),
];
}
$cache->endDataCache(['items' => $items]);
}
Агрегация и группировка
use Bitrix\Main\Entity\ExpressionField;
// Средняя цена по брендам
$result = $entity::getList([
'select' => [
'BRAND_' => 'BRAND',
new ExpressionField('AVG_PRICE', 'AVG(%s)', ['PRICE.VALUE']),
new ExpressionField('ITEM_COUNT', 'COUNT(%s)', ['ID']),
],
'filter' => ['=ACTIVE' => 'Y'],
'group' => ['BRAND.VALUE'],
]);
foreach ($result as $row) {
echo $row->get('BRAND_VALUE') . ': средняя цена ' . round($row->get('AVG_PRICE')) . ' ₽';
echo ' (' . $row->get('ITEM_COUNT') . ' товаров)';
}
Производительность: старый vs новый API
| Операция | CIBlockElement::GetList | D7 ORM |
|---|---|---|
| Выборка 100 элементов | ~50 мс | ~30 мс |
| Фильтр по свойству | JOIN на каждое свойство | Одна таблица (2.0) |
| Автокомплит IDE | Нет | Да (через аннотации) |
| Типизация | Массивы | Объекты |
| SQL-профилирование | Сложно | getLastQuery() |
Главный выигрыш в производительности — хранение свойств. В старом режиме каждое свойство — это отдельный JOIN с таблицей b_iblock_element_property. В режиме 2.0 все свойства — в одной строке отдельной таблицы.
Миграция со старого API
Не нужно переписывать всё сразу. Стратегия:
- Новый код — пишем на D7 ORM
- Критичные выборки — мигрируем при оптимизации
- Старый код — трогаем только при багфиксах
Оба API работают параллельно. Инфоблок в режиме 2.0 совместим со старым CIBlockElement::GetList.
Есть идея? Реализуем
Разрабатываем проекты, которые решают задачи бизнеса — от лендинга до сложного сервиса. Расскажите о своей задаче, подберём решение.

