D7 против старого ядра Битрикса: что брать под какие задачи
D7 не заменил старое ядро, а лёг вторым слоем поверх той же базы. Разбираем, где ORM выигрывает, где она молча теряет права доступа и бизнес-логику, и как разделить слои по модулям без переписывания проекта.

Вы открываете чужой проект на 1С-Битрикс, видите в компоненте CIBlockElement::GetList и тянетесь переписать на ORM — потому что D7 новее. Через день каталог начинает отдавать элементы, закрытые правами доступа для текущей группы, свойства приходится добирать вторым запросом, а менеджер пишет, что товары перестали попадать в поиск. Очевидное решение «переехать на D7» даёт проблему именно там, где старое ядро делало за вас больше, чем вы думали.
Что вы на самом деле выбираете
D7 появился в ядре main серии 14.x и с тех пор живёт рядом со старым API, а не вместо него. Это не миграция версий: оба слоя лежат в одной установке, работают с одними таблицами и вызывают одни и те же события модулей. Старые классы вида CIBlockElement, CUser, CPHPCache никто не удалял, компоненты по-прежнему наследуют CBitrixComponent, а $APPLICATION остаётся глобальным объектом.
Разница в трёх вещах: пространства имён и автозагрузка вместо глобальных классов, ORM1 с объектными результатами вместо CDBResult, и единый Result с методами isSuccess() и getErrorMessages() вместо «вернул false, смотри свойство объекта». Для части модулей — в первую очередь для sale — старое API объявлено устаревшим, и новая функциональность туда уже не доезжает. Для iblock ситуация обратная: D7-слой там заметно тоньше старого.
D7 не отменяет старое ядро, он добавляет второй слой над той же базой. Проекты ломает не выбор слоя, а смешивание двух слоёв внутри одной операции.
Подход 1. Остаться на старом ядре
Вы пишете так, как написан проект: CIBlockElement, CCatalogProduct, AddEventHandler в init.php. Выигрыш — предсказуемость. Выборка элементов через старое API учитывает права доступа к инфоблокам, умеет фильтровать по свойствам в одном вызове и отдаёт их как PROPERTY_<CODE>_VALUE. Запись через CIBlockElement::Add и Update тянет за собой бизнес-логику модуля: события, индексацию поиска, пересчёт связанных данных.
// iblock, старое ядро: свойства и права доступа из коробки
\Bitrix\Main\Loader::includeModule('iblock');
$iterator = \CIBlockElement::GetList(
['SORT' => 'ASC'],
['IBLOCK_ID' => 5, 'ACTIVE' => 'Y', '>=PROPERTY_PRICE' => 1000],
false,
['nTopCount' => 20],
['ID', 'NAME', 'PROPERTY_ARTICLE']
);
// Fetch() отдаёт значения как в базе,
// GetNext() дополнительно прогоняет их через htmlspecialchars
while ($element = $iterator->Fetch()) {
echo $element['ID'] . ' ' . $element['PROPERTY_ARTICLE_VALUE'] . PHP_EOL;
}
Чем платите: нет типизации и подсказок IDE по полям, ошибки приходят как false плюс текст в LAST_ERROR или в $APPLICATION->GetException(), сложные выборки с джойнами собираются руками. И часть новых возможностей платформы просто не имеет старого интерфейса.
Подход 2. Писать всё на D7
ORM-сущности, именованные параметры select/filter/order/limit, объектные результаты, встроенный кеш запроса. На своих таблицах это объективно лучше: наследуете DataManager, описываете карту полей один раз и получаете валидацию, связи и события сущности.
use Bitrix\Iblock\ElementTable;
use Bitrix\Main\Loader;
Loader::includeModule('iblock');
$rows = ElementTable::getList([
'select' => ['ID', 'NAME', 'IBLOCK_ID'],
'filter' => ['=IBLOCK_ID' => 5, '=ACTIVE' => 'Y'],
'order' => ['SORT' => 'ASC'],
'limit' => 20,
'cache' => ['ttl' => 3600],
])->fetchAll();
И тут же цена. ElementTable — это таблица, а не бизнес-сущность: прав доступа она не знает, значений свойств в ней нет. Свойства по символьным кодам доступны через сгенерированные ORM-классы инфоблоков в пространстве Bitrix\Iblock\Elements — они появляются, когда у инфоблока задан символьный код API (поле API_CODE), и доступны начиная с серии iblock 20.x. Точный синтаксис выборки свойств через эти классы сверяйте в документации по вашей версии модуля: он отличается от привычного PROPERTY_* и менялся по ходу развития ORM. Объектный доступ (fetchObject, fetchCollection) на ядрах старше серии 18.x тоже может отсутствовать — проверьте версию модуля main в обновлениях платформы.
Что D7 даёт бесплатно: обработка ошибок
// Старое ядро: признак ошибки — false, текст — в свойстве объекта
$element = new \CIBlockElement();
$newId = $element->Add(['IBLOCK_ID' => 5, 'NAME' => 'Новый элемент', 'ACTIVE' => 'Y']);
if (!$newId) {
throw new \RuntimeException($element->LAST_ERROR);
}
// D7: любая операция возвращает Result — ваша ORM-сущность
$result = \Vendor\Project\ImportLogTable::add([
'ELEMENT_ID' => $newId,
'CREATED_AT' => new \Bitrix\Main\Type\DateTime(),
]);
if (!$result->isSuccess()) {
throw new \RuntimeException(implode('; ', $result->getErrorMessages()));
}
Подход 3. Гибрид по модулям
Рабочий вариант для живого проекта: выбирать слой не по возрасту кода, а по модулю и по операции. Чтение и агрегации — ORM там, где не нужны права доступа и свойства. Запись в инфоблоки — через старое API, чтобы не обойти бизнес-логику модуля. Заказы, корзина, скидки — только D7, потому что старое API заказов устарело и новые механизмы в него не приходят.
use Bitrix\Sale\Order;
use Bitrix\Main\Loader;
Loader::includeModule('sale');
$order = Order::load($orderId);
$setResult = $order->setField('STATUS_ID', 'F');
if ($setResult->isSuccess()) {
$saveResult = $order->save();
if (!$saveResult->isSuccess()) {
// дальше ваша обработка: лог, уведомление, откат
}
}
Что чем делать
| Задача | Старое ядро | D7 | Что брать |
|---|---|---|---|
| Чтение элементов со свойствами и правами | CIBlockElement::GetList | ORM-классы инфоблоков с API_CODE | Старое, если нужны права доступа |
| Запись элементов и разделов | CIBlockElement, CIBlockSection | ElementTable, SectionTable | Старое: ORM пишет в таблицу мимо логики модуля |
| Заказы, корзина, доставка | CSaleOrder и родственники (устарели) | Bitrix\Sale\Order, Basket | D7, без вариантов |
| Свои таблицы | прямые SQL-запросы | DataManager | D7 |
| Регистрация обработчиков событий | AddEventHandler | EventManager::addEventHandler | Любой, но один стиль на проект |
| Кеш данных | CPHPCache | Bitrix\Main\Data\Cache, тегированный кеш | D7 |
| Настройки модулей | COption::GetOptionString | Bitrix\Main\Config\Option | D7 |
| Транзакции | $DB->StartTransaction() | Application::getConnection() | D7 |
Когда что выбирать: критерии, а не вкус
- Операция меняет данные модуля. Берите API модуля — старое для инфоблоков, D7 для заказов. Таблица ORM здесь не интерфейс, а хранилище.
- Выборка должна видеть только то, что видит пользователь. Старое ядро. ORM прав доступа к инфоблокам не проверяет.
- Нужны джойны, группировки, подсчёты по нескольким таблицам. D7: один
getListвместо цикла запросов. - Код новый и изолированный — обработчик, агент, свой модуль, REST-контроллер. Пишите на D7 целиком, смешивать нечего.
- Код внутри legacy-компонента, который правят ещё двое. Оставайтесь в стиле файла. Половинчатый переезд обходится дороже, чем единообразный старый код.
Что ломается на стыке слоёв
- Тихо исчезающие права доступа. Переписали выборку каталога на
ElementTable— и в выдачу попали элементы инфоблоков, закрытых для группы. Баг не падает с ошибкой, его находят пользователи. - Запись мимо бизнес-логики. Прямой
add/updateпо таблице инфоблока не поднимает события модуля, не обновляет поисковый индекс и не трогает связанные структуры. Внешне всё сохранилось, а поиск и фасеты отстали от базы. - Разные наборы событий. События ORM-сущности и события модуля — не одно и то же. Обработчик, подписанный на
OnAfterIBlockElementUpdate, не сработает на записи через ORM, и наоборот. - Экранирование.
GetNext()прогоняет значения черезhtmlspecialchars,Fetch()и ORM — нет. При переезде на D7 проверьте шаблоны: там, где раньше приходил безопасный текст, теперь приходит сырой. - Кеш в двух системах. Компонентный кеш,
cacheвgetListи тегированный кеш живут отдельно. Сброс одного не сбрасывает остальные, и на проде это выглядит как «изменения не применяются».
Что делать дальше
Начните с инвентаризации: выпишите версии модулей main, iblock, sale на проекте — от них зависит, какие ORM-возможности вам вообще доступны. Дальше примите одно правило на команду и запишите его в README: какой слой для какой операции, чтобы спор не повторялся на каждом ревью. Новый код — на D7 без исключений; старый трогайте только там, где меняете поведение, а не ради стиля.
На проде смотрите на два показателя после любого перевода выборки на ORM: число запросов на странице (панель отладки Битрикса показывает его вместе со временем) и совпадение выдачи каталога под авторизованным и анонимным пользователем. Второе ловит потерю прав доступа раньше, чем клиент. Миграция компонентов на D7-контроллеры и организация своих модулей — отдельная тема, здесь мы разбирали только выбор слоя для работы с данными.
1 ORM (object-relational mapping) — слой, который описывает таблицу базы данных как класс с картой полей, а строки — как массивы или объекты. В Битриксе это классы-наследники DataManager с именами на Table: ElementTable, PriceTable и ваши собственные. Актуальный состав методов смотрите в документации по D7.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: 1С-Битрикс: документация по ядру D7, 1С-Битрикс: документация по старому ядру (api_help)
Частые вопросы
Старое ядро скоро отключат? Нужно срочно всё переписывать?
А если у меня ядро старее и нужных ORM-классов нет?
Почему после перехода на ORM в каталоге появились лишние товары?
Можно ли создавать элементы инфоблока через ORM?
Что из этого ломается именно в проде, а не на тестовом стенде?
Как обрабатывать ошибки, если в проекте оба слоя?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.