01 01234567890 01234567890

Bitrix24 D7 ORM: работа с базой данных и собственными таблицами

Разработка сайта 19 August 2026

Ручные SQL-запросы через CDatabase в старом ядре Bitrix24 — это боль: любая опечатка в поле даёт ошибку в продакшене, а миграции превращаются в головную боль. В статье разбираем, как современный слой D7 ORM убирает эти проблемы: вместо строк SQL — обычный PHP-код, защита от инъекций, валидация и связи между таблицами.

Когда модуль на старом ядре Bitrix24 (или 1С-Битрикс) пишет SQL-запросы вручную через CDatabase, любая опечатка в названии поля превращается в ошибку, которую видно только в продакшене, а миграция базы между серверами — в ручную головную боль. D7 — современный слой работы с базой данных в Bitrix24, и его ORM (Object-Relational Mapping — связывание объектов кода с таблицами базы данных) убирает большую часть этой боли: вместо строк SQL пишете обычный PHP-код, а фреймворк сам собирает запрос и защищает от SQL-инъекций.

В статье разберём, как работать с базой данных через D7 ORM в Bitrix24: создание собственных таблиц, базовые операции и частые ошибки при переходе со старого подхода. Материал для разработчиков модулей и интеграций, которым нужно подключить bitrix24 d7 orm для работы с базой данных и собственными таблицами.

Что понадобится перед началом

  • Доступ к коду модуля Bitrix24 (свой модуль или возможность подключать классы в существующем)
  • PHP 7.4+ и понимание объектно-ориентированного программирования (классы, наследование)
  • Права на изменение структуры базы данных на тестовом портале — создание новых таблиц нельзя тестировать на проде без бэкапа
  • Базовое понимание SQL (даже если ORM скрывает большую часть запросов, понимание того, что происходит «под капотом», экономит часы отладки)

Основная часть: работаем с D7 ORM

Шаг 1. Создаём класс таблицы

В D7 каждая таблица описывается отдельным классом, наследующим Bitrix\Main\Entity\DataManager. Класс размещается в папке lib модуля и описывает структуру таблицы — имя и список полей.

<?php
namespace Vendor\Module;

use Bitrix\Main\Entity\DataManager;
use Bitrix\Main\Entity\StringField;
use Bitrix\Main\Entity\IntegerField;
use Bitrix\Main\Entity\DatetimeField;

class EquipmentRentalTable extends DataManager
{
    // Имя таблицы в базе данных
    public static function getTableName()
    {
        return 'vendor_equipment_rental';
    }

    // Описание полей таблицы
    public static function getMap()
    {
        return [
            new IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
            new StringField('EQUIPMENT_NAME', ['required' => true]),
            new IntegerField('CLIENT_ID', ['required' => true]),
            new DatetimeField('RENT_DATE'),
        ];
    }
}

Частая ошибка: забывают указать 'primary' => true, 'autocomplete' => true для поля ID. Без этого ORM не понимает, какое поле первичный ключ, и операции добавления записей будут падать с непонятной ошибкой.

Шаг 2. Создаём таблицу в базе данных

Таблица не появляется в базе автоматически при создании класса — нужно явно вызвать создание структуры, обычно это делается в файле установки модуля.

// Вызывается один раз при установке модуля
if (!\Vendor\Module\EquipmentRentalTable::getEntity()->getConnection()->isTableExists(
    \Vendor\Module\EquipmentRentalTable::getTableName()
)) {
    \Vendor\Module\EquipmentRentalTable::getEntity()->createDbTable();
}

Шаг 3. Добавляем записи

$result = EquipmentRentalTable::add([
    'EQUIPMENT_NAME' => 'Экскаватор JCB',
    'CLIENT_ID' => 1024,
    'RENT_DATE' => new \Bitrix\Main\Type\DateTime(),
]);

if ($result->isSuccess()) {
    $newId = $result->getId();
} else {
    // Список ошибок валидации, если required-поле не заполнено
    $errors = $result->getErrorMessages();
}

Частая ошибка: не проверяют isSuccess() перед использованием getId(). Если обязательное поле не передано, add не выбросит исключение — он вернёт объект результата с ошибкой, и без проверки код продолжит работать с пустым ID.

Шаг 4. Читаем данные через getList

$rows = EquipmentRentalTable::getList([
    'select' => ['ID', 'EQUIPMENT_NAME', 'RENT_DATE'],
    'filter' => ['CLIENT_ID' => 1024],
    'order' => ['RENT_DATE' => 'DESC'],
    'limit' => 10,
])->fetchAll();

foreach ($rows as $row) {
    echo $row['EQUIPMENT_NAME'] . ' — ' . $row['RENT_DATE'] . PHP_EOL;
}

Частая ошибка: вызывают getList без limit на таблицах, которые потенциально могут содержать десятки тысяч записей. Это не упадёт сразу, но при росте данных запрос начнёт серьёзно замедлять страницу — пагинацию нужно закладывать с самого начала, а не добавлять потом.

Шаг 5. Обновляем и удаляем записи

// Обновление записи по ID
EquipmentRentalTable::update(42, ['EQUIPMENT_NAME' => 'Экскаватор Komatsu']);

// Удаление записи по ID
EquipmentRentalTable::delete(42);

Шаг 6. Используем связи между таблицами (reference)

D7 ORM поддерживает связи между таблицами, аналогично foreign key, через Bitrix\Main\Entity\ReferenceField. Это позволяет в одном запросе подтянуть данные из связанной таблицы, например, данные клиента из CRM по CLIENT_ID.

use Bitrix\Main\Entity\ReferenceField;
use Bitrix\Main\Entity\Query\Join;

// В методе getMap() добавляем связь
new ReferenceField(
    'CLIENT',
    \Bitrix\Crm\ContactTable::class,
    Join::on('this.CLIENT_ID', 'ref.ID')
),

После этого в getList можно указывать 'select' => ['*', 'CLIENT_.NAME'] и получать данные клиента без отдельного запроса.

Шаг 7. Добавляем индексы для производительности

Если таблица растёт и фильтрация по CLIENT_ID становится частой операцией, без индекса каждый запрос будет сканировать всю таблицу. Индексы задаются в методе getMap через дополнительный параметр при создании таблицы или прямым SQL-запросом при установке модуля.

Частые ошибки и как их избежать

Ошибка: смешивают D7 ORM с прямыми SQL-запросами через старое API CDatabase в одном модуле. Это работает, но усложняет поддержку — часть логики защищена от инъекций через ORM, часть — нет, если в SQL-запросы не экранированы значения. Решение: выбрать единый подход в рамках модуля, переходить на D7 полностью, если модуль разрабатывается с нуля.

Ошибка: не учитывают, что getList без явного select тянет все поля таблицы, включая ненужные. При большом количестве полей и записей это создаёт лишнюю нагрузку на базу и память PHP. Решение: всегда явно указывать select с нужными полями.

Ошибка: создают таблицу при каждом обращении к коду вместо разовой установки. Возникает из попытки «на всякий случай» проверять и создавать таблицу прямо в рабочем коде модуля, а не в установщике. Решение: создание структуры таблицы — это операция установки модуля, выполняется один раз, а не при каждом запросе пользователя.

Итог

Теперь у вас есть рабочий подход к созданию собственных таблиц в Bitrix24 через D7 ORM — с защитой от SQL-инъекций, валидацией и связями между сущностями, без необходимости писать SQL руками. Если на каком-то шаге застряли — опишите ситуацию в комментарии или напишите нам напрямую.

CTA

Нужна помощь с разработкой модуля под Bitrix24 — запишитесь на консультацию.

Разработка сайта 19.08.2026 Азамат
Все статьи
«Назовите точную сумму» — а система-то ещё не придумана
Следующая статья 23.07.2026