Собственная ORM-сущность в Битрикс — это класс-наследник DataManager с описанием таблицы. Создав его, вы получаете типизированный CRUD, валидацию и события без единой строки ручного SQL.
Подготовка: где разместить класс
ORM-сущность живёт внутри модуля. Даже если вы не планируете полноценный тиражный модуль, оформить сущность как часть модуля правильнее, чем класть в /local/php_interface/. Так работает автозагрузка и легче переносить код между проектами.
- каталог модуля:
/local/modules/vendor.catalog/; - код сущности:
/local/modules/vendor.catalog/lib/brandtable.php; - namespace класса:
Vendor\Catalog\BrandTable.
BrandTable лежит в brandtable.php. Namespace выводится из идентификатора модуля vendor.catalog по правилу: точка меняется на разделитель, части приводятся к CamelCase.Скелет Table-класса
Минимальный класс реализует два статических метода: имя таблицы и карту полей. Уже этого достаточно, чтобы получить рабочий CRUD.
namespace Vendor\Catalog;
use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\ORM\Fields\IntegerField;
use Bitrix\Main\ORM\Fields\StringField;
use Bitrix\Main\ORM\Fields\BooleanField;
use Bitrix\Main\ORM\Fields\Validators\LengthValidator;
class BrandTable extends DataManager
{
public static function getTableName(): string
{
return 'vendor_catalog_brand';
}
public static function getMap(): array
{
return [
(new IntegerField('ID'))->configurePrimary(true)->configureAutocomplete(true),
(new StringField('CODE'))->configureRequired(true)
->addValidator(new LengthValidator(1, 50)),
(new StringField('NAME')),
(new BooleanField('ACTIVE'))->configureValues('N', 'Y')->configureDefaultValue('Y'),
];
}
}Обратите внимание на fluent-конфигураторы (configurePrimary, configureRequired и т. д.) — это современный способ описания полей в новых версиях main вместо массива параметров в конструкторе.
Типы полей ORM
Каждое поле — объект класса из пространства Bitrix\Main\ORM\Fields. Тип определяет и SQL-колонку, и правила приведения значения при чтении/записи.
| Класс поля | Тип в БД | Значение в PHP |
|---|---|---|
IntegerField | int | int |
StringField | varchar/text | string |
BooleanField | char/varchar | bool (маппинг N/Y) |
FloatField | double | float |
DatetimeField | datetime | Type\DateTime |
DateField | date | Type\Date |
EnumField | varchar | string из списка |
TextField | text | string |
Тип поля критичен: BooleanField автоматически превращает хранимые N/Y в false/true, а DatetimeField отдаёт объект даты, а не строку.
Создание таблицы в БД
Table-класс описывает схему, но саму таблицу нужно создать. ORM умеет генерировать DDL по карте полей — это удобно при установке модуля.
use Vendor\Catalog\BrandTable;
use Bitrix\Main\Application;
$connection = Application::getConnection();
if (!$connection->isTableExists(BrandTable::getTableName())) {
BrandTable::getEntity()->createDbTable();
}Метод createDbTable() сгенерирует CREATE TABLE из getMap(). Для боевых проектов лучше держать явный SQL в файле установки (install/db/mysql/install.sql), чтобы полностью контролировать индексы и типы колонок, которые ORM не всегда выводит оптимально.
createDbTable() создаёт только первичный ключ. Вторичные индексы под ваши фильтры нужно добавлять вручную — иначе выборки по неиндексированным полям деградируют на объёме.Валидаторы и значения по умолчанию
ORM проверяет данные перед записью. Валидаторы навешиваются на поле методом addValidator() либо конфигурацией validation. Есть готовые классы: LengthValidator, RegExpValidator, RangeValidator, UniqueValidator.
configureRequired(true)— поле обязательно при добавлении;configureDefaultValue()— значение по умолчанию, если не передано;- кастомный валидатор — замыкание или метод, возвращающий
trueили текст ошибки; - ошибки собираются в
Resultи доступны черезgetErrorMessages().
(new StringField('CODE'))
->configureRequired(true)
->addValidator(new LengthValidator(2, 50))
->addValidator(new RegExpValidator('/^[a-z0-9_-]+$/'))
События сущности
ORM-сущность генерирует события на каждом этапе CRUD: onBeforeAdd, onAdd, onAfterAdd, аналогично для update и delete. Их описывают методами прямо в Table-классе — не нужно регистрировать глобально.
use Bitrix\Main\ORM\Event;
use Bitrix\Main\ORM\EventResult;
public static function onBeforeAdd(Event $event): EventResult
{
$result = new EventResult();
$data = $event->getParameter('fields');
$result->modifyFields([
'CODE' => mb_strtolower($data['CODE']),
]);
return $result;
}Метод modifyFields() позволяет нормализовать данные до записи. Для отмены операции с ошибкой используют $result->addError(). Это удобное место для генерации slug, установки автора или проверки бизнес-правил.
Использование готовой сущности
После описания класс сразу готов к работе. Ниже — типичный цикл жизни записи.
use Vendor\Catalog\BrandTable;
$add = BrandTable::add(['CODE' => 'ACME', 'NAME' => 'ACME']);
$id = $add->getId();
$row = BrandTable::getByPrimary($id, ['select' => ['*']])->fetch();
BrandTable::update($id, ['ACTIVE' => 'N']);
BrandTable::delete($id);- опишите Table-класс и создайте таблицу при установке;
- используйте CRUD-методы, проверяя
Result; - нормализуйте данные в событиях, а не в каждом вызове;
- добавьте нужные индексы под реальные фильтры.
Итог
Создать свою ORM-сущность в Битрикс — это описать Table-класс с getTableName() и getMap(), выбрать правильные типы полей, создать таблицу с нужными индексами, навесить валидаторы и, при необходимости, обработчики событий. После этого CRUD, валидация и связи доступны без ручного SQL.
В B2Bsite мы проектируем доменные модели на ORM для каталогов, обменов и B2B-порталов: аккуратные сущности с продуманными индексами и событиями. Нужен собственный модуль обмена или сложная модель данных — соберём её надёжно и с прицелом на рост.
Частые вопросы
Что минимально нужно для ORM-сущности?
Класс-наследник DataManager с двумя методами: getTableName возвращает имя таблицы, getMap возвращает массив полей. Этого достаточно для готового CRUD.
Где размещать Table-класс?
Внутри модуля, обычно /local/modules/vendor.catalog/lib/. Имя файла в нижнем регистре повторяет имя класса, а namespace выводится из идентификатора модуля.
Как создать таблицу в БД по карте полей?
Через BrandTable::getEntity()->createDbTable(). Он генерирует CREATE TABLE из getMap, но создаёт только первичный ключ. Вторичные индексы добавляют вручную.
Какие типы полей есть в ORM?
IntegerField, StringField, TextField, BooleanField, FloatField, DateField, DatetimeField, EnumField и другие из Bitrix\Main\ORM\Fields. Тип определяет и колонку в БД, и приведение значения в PHP.
Как добавить валидацию поля?
Методом addValidator с готовым классом (LengthValidator, RegExpValidator, RangeValidator) или замыканием. Ошибки собираются в Result и доступны через getErrorMessages.
Как перехватить сохранение записи?
Описать в Table-классе статические методы событий onBeforeAdd, onBeforeUpdate и т. д. Через EventResult можно менять поля методом modifyFields или добавлять ошибку.
Нужно ли вручную писать SQL для таблицы?
Не обязательно: ORM сгенерирует DDL. Но для боевых проектов удобнее хранить явный install.sql, чтобы контролировать типы колонок и индексы точнее, чем это делает генератор.
Поможем с настройкой и поддержкой Для разработчиков
Поможем с настройкой, доработкой и поддержкой Для разработчиков.