ORM в Битрикс D7: работа с таблицами, связи и сложные запросы

ORM (Object-Relational Mapping) в Битрикс D7 — это слой для работы с базой данных без написания SQL. Вместо raw-запросов вы описываете структуру таблиц в классах и работаете через типизированный API.

Создание ORM-класса

namespace MyModule;

use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\ORM\Fields;

class OrderLogTable extends DataManager
{
    public static function getTableName(): string
    {
        return 'b_my_order_log';
    }

    public static function getMap(): array
    {
        return [
            new Fields\IntegerField('ID', [
                'primary'     => true,
                'autocomplete'=> true,
            ]),
            new Fields\IntegerField('ORDER_ID', [
                'required' => true,
            ]),
            new Fields\StringField('ACTION', [
                'required'  => true,
                'validation'=> function() {
                    return [new Fields\Validators\LengthValidator(1, 255)];
                },
            ]),
            new Fields\DatetimeField('DATE_CREATE', [
                'default_value' => function() {
                    return new \Bitrix\Main\Type\DateTime();
                },
            ]),
            new Fields\TextField('COMMENT'),
        ];
    }
}

Создание таблицы

// При установке модуля создаём таблицу
$connection = \Bitrix\Main\Application::getInstance()->getConnection();
if (!$connection->isTableExists(OrderLogTable::getTableName())) {
    OrderLogTable::getEntity()->createDbTable();
}

CRUD через ORM

// Добавление
$result = OrderLogTable::add([
    'ORDER_ID'   => 42,
    'ACTION'     => 'status_changed',
    'COMMENT'    => 'Статус изменён на "Доставлен"',
]);

// Выборка с фильтром
$rows = OrderLogTable::getList([
    'select' => ['ID', 'ORDER_ID', 'ACTION', 'DATE_CREATE'],
    'filter' => [
        '=ORDER_ID'        => 42,
        '>=DATE_CREATE'    => (new \Bitrix\Main\Type\DateTime())->add('T-24H'),
    ],
    'order'  => ['DATE_CREATE' => 'DESC'],
    'limit'  => 50,
])->fetchAll();

// Обновление
OrderLogTable::update(15, ['COMMENT' => 'Обновлённый комментарий']);

// Удаление
OrderLogTable::delete(15);

Связи между таблицами

// В getMap() добавляем Reference
new Fields\Relations\Reference(
    'ORDER',
    \Bitrix\Sale\Internals\OrderTable::class,
    \Bitrix\Main\ORM\Query\Join::on('this.ORDER_ID', 'ref.ID')
),

// Теперь можно выбирать данные заказа вместе с логом
$rows = OrderLogTable::getList([
    'select' => ['ID', 'ACTION', 'ORDER__ACCOUNT_NUMBER', 'ORDER__DATE_INSERT'],
    'filter' => ['=ORDER.STATUS_ID' => 'N'],
])->fetchAll();

Query Builder для сложных запросов

use Bitrix\Main\ORM\Query\Query;

$query = OrderLogTable::query()
    ->setSelect(['ORDER_ID', new Fields\ExpressionField('CNT', 'COUNT(%s)', 'ID')])
    ->setFilter(['>=DATE_CREATE' => (new \Bitrix\Main\Type\DateTime())->add('T-7D')])
    ->setGroup(['ORDER_ID'])
    ->setOrder(['CNT' => 'DESC'])
    ->setLimit(10);

$result = $query->exec()->fetchAll();
// [['ORDER_ID' => 42, 'CNT' => 15], ...]

Транзакции

$connection = \Bitrix\Main\Application::getInstance()->getConnection();
$connection->startTransaction();

try {
    OrderLogTable::add(['ORDER_ID' => 1, 'ACTION' => 'created']);
    // другие операции
    $connection->commitTransaction();
} catch (\Exception $e) {
    $connection->rollbackTransaction();
    throw $e;
}

Итог

ORM Битрикс D7 — мощный инструмент когда нужна типизация и связи. Для простых CRUD операций он удобнее raw SQL. Для очень сложных аналитических запросов лучше писать SQL напрямую через $connection->query().