Как работать с Highload-блоком через D7 ORM: compileEntity, getList, add и delete?

Как работать с Highload-блоком через D7 ORM: compileEntity, getList, add и delete?

На практике типичная ситуация: Highload-блок с городами уже создан в админке, но на проде в шаблоне всплывает Class not found, а getList возвращает пустой массив. Например, в git остался HLBLOCK_ID=3 с локалки — это проблема переноса между окружениями: в агенте compileEntity вызывают в каждой итерации цикла, а add() с пустым UF_CITY молча не сохраняет запись. Ниже — рабочая цепочка D7 ORM для CMS: от compileEntity до add/update/delete с isSuccess(), фабрика entityClass без N+1 и переносимый код по NAME вместо жёсткого ID.

Highload в коде - это не админка, а DataClass после compileEntity. Один HLBlockFactory на проект кэширует класс на HTTP-хит, resolveHighloadblock('Cities') переживает деплой без смены ID, getList/add/update/delete проверяют isSuccess() и getErrorMessages(). Удаление записи - только $entityClass::delete($id), не HighloadBlockTable::delete.

Материал про коробочную 1С-Битрикс: Управление сайтом. Облачный Bitrix24 со своими сущностями - другой продукт, здесь только модуль highloadblock. Создание HL-блока в админке уже разобрано в гайде по Highload - эта статья начинается там, где блок уже есть, и нужен PHP-код.

Определите, когда нужен HL ORM в коде, а когда хватит инфоблока или своей таблицы

Сравнительная таблица: когда Highload ORM, инфоблок D7 или своя таблица в Bitrix

Highload-блок - справочник или лог без иерархии разделов: города, статусы заказов, коды складов. Данные лежат в отдельной таблице с полями UF_*, а PHP-класс для CRUD ядро собирает на лету через compileEntity. Если задача - новости, каталог с SEO или дерево разделов, берите инфоблок D7. Если нужна полностью своя схема под модуль - своя *Table в lib/.

Задача Инфоблок D7 Highload ORM Своя *Table в модуле Только админка
Справочник 20 000 городов Медленно, лишние сущности Да - отдельная таблица HL Избыточно Нет API в коде
Новости и статьи блога Да - ElementTable Нет Нет Редко
Очередь задач модуля Нет Иногда Да - полный контроль Нет
Редактирование менеджером в админке Да Да - HL-список в админке Нет UI Да, без PHP

Выборка инфоблока - в сравнении CIBlockElement и D7 ORM, своя таблица DataManager - в гайде по *Table в модуле. HL ORM подключают, когда справочник уже в Highload и нужен getList/add из компонента, агента или REST.

Делайте: храните плоские справочники в HL и работайте через DataClass. Не делайте: не тащите 50 000 SKU в инфоблок только ради привычки - нагрузка на индексы и кеш вырастет.

Получите DataClass через compileEntity и вынесите фабрику в lib/ модуля

Схема workflow: compileEntity, getDataClass и HLBlockFactory для Highload в Bitrix D7

ORM (Object-Relational Mapping) в D7 - слой, который связывает PHP-класс с таблицей MySQL. Для HL класс не пишут руками: HighloadBlockTable::compileEntity() собирает DataManager с полями UF_* и возвращает имя класса через getDataClass(). Повторный compileEntity на одном хите может пересоздать класс через eval - держите результат в static cache.

  1. Подключите модуль: Loader::includeModule('highloadblock').
  2. Найдите блок по символьному NAME через HighloadBlockTable::resolveHighloadblock('Cities') или по TABLE_NAME в getList.
  3. Скомпилируйте сущность: $entity = HighloadBlockTable::compileEntity($hlblock); $dataClass = $entity->getDataClass().
  4. Вынесите логику в HLBlockFactory в lib/ кастомного модуля - см. создание модуля.
  5. Закэшируйте $dataClass в static-массиве по NAME или TABLE_NAME на весь HTTP-запрос.
  6. Проверьте на staging: тот же код без правки HLBLOCK_ID после деплоя с локалки.
<?php
use Bitrix\Main\Loader;
use Bitrix\Highloadblock\HighloadBlockTable;

class HLBlockFactory
{
    private static array $cache = [];

    public static function getDataClass(string $hlName): string
    {
        if (isset(self::$cache[$hlName])) {
            return self::$cache[$hlName];
        }
        Loader::includeModule('highloadblock');
        $hlblock = HighloadBlockTable::resolveHighloadblock($hlName);
        if (!$hlblock) {
            throw new \RuntimeException('HL block not found: ' . $hlName);
        }
        $entity = HighloadBlockTable::compileEntity($hlblock);
        return self::$cache[$hlName] = $entity->getDataClass();
    }
}

Схема: resolveHighloadblock → compileEntity → getDataClass → static cache → единый сервис в lib/.

Делайте: один getDataClass() на проект. Не делайте: не копируйте compileEntity в result_modifier, агент и REST - Class not found на проде почти гарантирован.

Настройте getList: select, filter, order и проверьте пустую выборку

Чеклист настройки getList для Highload: select, filter, order и проверка пустой выборки

После получения $dataClass выборка идёт как у любого DataManager: getList с ключами select, filter, order, limit. Поля пользователя всегда с префиксом UF_: UF_NAME, UF_ACTIVE. Фильтр по точному совпадению - '=UF_ACTIVE' => 'Y', по подстроке - '%UF_NAME' => 'Моск'.

$dataClass = HLBlockFactory::getDataClass('Cities');
$rows = $dataClass::getList([
    'select' => ['ID', 'UF_NAME', 'UF_REGION'],
    'filter' => ['=UF_ACTIVE' => 'Y'],
    'order'  => ['UF_NAME' => 'ASC'],
    'limit'  => 50,
]);
while ($row = $rows->fetch()) {
    // $row['UF_NAME']
}

Если массив пустой при "правильном" коде, проверьте TABLE_NAME, префикс UF_ в filter/select и значение UF_ACTIVE (Y/N). Class ... was not found обычно значит неверный ID на окружении - переходите на resolveHighloadblock. Связь двух HL - через runtime Reference в getList, см. документацию highloadblock.

Делайте: явный select только нужных UF_*. Не делайте: не вызывайте compileEntity внутри while по офисам - CPU и лишний destroy класса.

Выполните add, update и delete с проверкой isSuccess()

Запись и изменение принимают массив полей с UF_*. Методы возвращают Result. Без isSuccess() ошибка по обязательному UF-полю проглатывается, и кажется, что "ничего не произошло".

$dataClass = HLBlockFactory::getDataClass('Cities');
$result = $dataClass::add([
    'UF_NAME'   => 'Казань',
    'UF_ACTIVE' => 'Y',
]);
if (!$result->isSuccess()) {
    throw new \RuntimeException(implode('; ', $result->getErrorMessages()));
}
$newId = $result->getId();

$dataClass::update($newId, ['UF_NAME' => 'Казань (Татарстан)']);
$del = $dataClass::delete($newId);
if (!$del->isSuccess()) {
    // логируем getErrorMessages()
}

Критично: HighloadBlockTable::delete($id) удаляет весь HL-блок со всеми записями. Строку справочника удаляют только через $dataClass::delete($id). Путаница описана в справочнике delete для HighloadBlockTable.

На события HL (OnBeforeAdd, OnAfterUpdate) можно повесить валидацию с ENTITY_ID из compileEntityId. После записи сбросьте кеш компонента.

Делайте: всегда обрабатывайте getErrorMessages(). Не делайте: не вызывайте HighloadBlockTable::delete, когда нужно убрать одну строку.

Различите compileEntityId и getDataClass для UF-полей

С версии 20.0.0 HighloadBlockTable::compileEntityId($id) возвращает ENTITY_ID для CUserTypeEntity при программном добавлении UF в HL. Это не getDataClass: compileEntityId - для миграции полей, compileEntity - для CRUD.

Делайте: фиксируйте обязательные UF_ в документации модуля. Не делайте: не путайте compileEntityId с именем DataClass.

Проверьте чек-лист перед выкладкой на prod

После настройки CRUD прогоните сценарий на staging и убедитесь, что код переносится без правки ID.

  1. Один HLBlockFactory или сервис в lib/, нет дублей compileEntity в шаблонах.
  2. resolveHighloadblock по символьному NAME вместо HLBLOCK_ID из локалки.
  3. getList возвращает ожидаемые UF_* с корректным filter.
  4. add/update с isSuccess() и понятным логом getErrorMessages().
  5. delete только через DataClass, не HighloadBlockTable::delete.
  6. Кеш компонента сброшен после изменения справочника.

Вы получите переносимый слой доступа к HL: один хелпер, предсказуемый CRUD и понятные ошибки вместо Class not found на проде. Если нужен аудит Highload на действующем проекте - обсудим задачу, примеры внедрений - в портфолио.

Автор: Максим Мольков, разработчик 1С-Битрикс.
Источники: документация D7 highloadblock, HighloadBlockTable::compileEntity, практика внедрений на CMS.

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

Чем Highload ORM отличается от инфоблока D7?

Инфоблок - контент с разделами, SEO и ElementTable. Highload - плоский справочник в отдельной таблице с UF_* и compileEntity. Для каталога статей берите инфоблок, для 20 000 городов - HL. Подробнее в сравнении GetList и D7 ORM.

Как получить DataClass по ID HL-блока?

Loader::includeModule('highloadblock'), затем $hl = HighloadBlockTable::getById($id)->fetch(), $entity = HighloadBlockTable::compileEntity($hl), $dataClass = $entity->getDataClass(). Надёжнее на prod - resolveHighloadblock('SymbolicName') без привязки к числовому ID.

Почему add() возвращает ошибку по UF-полю?

Проверьте обязательные UF_*, тип поля и формат значения. Вызовите getErrorMessages() на Result. Пустой UF_CITY для обязательного поля - типичная причина "тихого" отказа без isSuccess().

Как удалить запись Highload программно?

Получите $dataClass через фабрику, вызовите $dataClass::delete($elementId) и проверьте isSuccess(). HighloadBlockTable::delete удаляет весь блок - для одной строки справочника он не подходит.

Можно ли повесить обработчик на HL-блок как на инфоблок?

Да, события OnBeforeAdd, OnAfterUpdate и аналоги для HL существуют. Регистрируйте обработчик на модуль highloadblock с ENTITY_ID из compileEntityId. Логику валидации держите в одном месте, не дублируйте в каждом add().

Почему на prod Class not found, а на локалке работало?

Чаще всего другой HLBLOCK_ID или TABLE_NAME на окружении. Уберите жёсткий getById(3), перейдите на resolveHighloadblock по NAME и static cache compileEntity. Создание самого блока - в гайде по созданию Highload.

Читайте также

Интеграции с 1С и API
1072 6 мин.

Как настроить регистрацию и личный кабинет покупателя на 1С-Битрикс?

Пошаговая настройка регистрации на сайте битрикс: Главный модуль, main.register, system.auth.form и sale.personal.section с историей заказов и 152-ФЗ.
Интеграции с 1С и API
941 6 мин.

Как настроить скидки и промокоды на 1С-Битрикс: правила корзины и купоны

Пошаговый гайд по скидкам в CMS-магазине: скидка на товар, правило корзины от суммы, купоны с лимитом, приоритеты без конфликтов и тестовый заказ. Не Bitrix24.
Интеграции с 1С и API
757 15 мин.

Что такое компонент в Битриксе и как он работает

Каждый разработчик, впервые столкнувшийся с Битриксом, проходит через своеобразный обряд посвящения. Вначале кажется, что это просто CMS, где можно поправить HTML в визуальном редакторе или дописать пару строк CSS. Но однажды наступает момент, когда нужно изменить логику вывода новостей, отфильтровать товары по хитрому свойству или добавить на страницу нечто совершенно новое. И тут он впервые слышит это слово — «компонент». Для многих этот момент становится стеной. Система, казавшаяся понятной, вдруг превращается в черный ящик, полный непонятных файлов и странных переменных. Но стоит лишь раз заглянуть под капот, как эта стена рассыпается, превращаясь в набор удивительно логичных и мощных строительных блоков. Понимание компонентов — это тот самый щелчок, после которого разработка на Битрикс из мучения превращается в творчество.

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

Интеграции с 1С и API
714 2 мин.

Установка Composer в 1С-Битрикс

Установка Composer в проекте на 1С-Битрикс требует учета особенностей платформы, чтобы обеспечить корректную работу и интеграцию с системой.