Своя таблица на ORM - сущность, запросы, изменение структуры
Заводим собственную таблицу под данные проекта: описываем сущность, создаём таблицу при установке модуля и разбираемся, как менять её потом.
Решение
Описываем сущность классом:
namespace Vendor\Shop;
use Bitrix\Main\ORM\Data\DataManager;use Bitrix\Main\ORM\Fields\{IntegerField, StringField, DatetimeField};
class SyncLogTable extends DataManager{ // имя таблицы задают один раз: переименование ломает весь код проекта public static function getTableName(): string { return 'vendor_sync_log'; }
public static function getMap(): array { return [ new IntegerField('ID', ['primary' => true, 'autocomplete' => true]), new IntegerField('ORDER_ID', ['required' => true]), new StringField('STATUS', ['size' => 32]), new DatetimeField('CREATED_AT'), ]; }}Класс сущности остаётся единственным источником правды о структуре. Имя таблицы, состав полей и их типы живут в коде, поэтому новый стенд получает ту же структуру, что и боевой сайт, без ручного вмешательства.
Создаём таблицу при установке модуля:
public function DoInstall() { \Bitrix\Main\Loader::includeModule('main'); \Vendor\Shop\SyncLogTable::getEntity()->createDbTable(); // индексы добавляют отдельным запросом: сущность их не создаёт $conn = \Bitrix\Main\Application::getConnection(); $conn->query('CREATE INDEX ix_sync_order ON vendor_sync_log (ORDER_ID)');}Таблицу создаёт установка модуля, а вовсе не человек своими руками. Индексы при этом добавляют отдельно: описание сущности их не создаёт, а без указателя по полю отбора таблица тормозит уже на сотне тысяч строк.
Пишем и читаем данные:
SyncLogTable::add(['ORDER_ID' => $orderId, 'STATUS' => 'sent', 'CREATED_AT' => new \Bitrix\Main\Type\DateTime()]);$rows = SyncLogTable::getList([ 'select' => ['ID', 'STATUS', 'CREATED_AT'], 'filter' => ['=ORDER_ID' => $orderId, '>CREATED_AT' => $since], 'order' => ['ID' => 'DESC'], 'limit' => 50,])->fetchAll();Запросы идут через методы сущности, а не строками с прямым обращением к базе. Выигрыш здесь не в красоте: связи, типы и экранирование значений платформа берёт на себя, а прямой запрос оставляет это автору.
Меняем структуру через обновление модуля:
// скрипт обновления: сначала база, потом описание сущности в коде$conn->query('ALTER TABLE vendor_sync_log ADD COLUMN ATTEMPT INT DEFAULT 0');// правка только getMap оставляет базу прежней и ломает запросыСтруктуру меняют в двух местах сразу: в базе и в описании сущности. Правка одного описания даёт ошибку неизвестного поля, а правка одной базы - поле, о котором код не знает.
Старые записи из журналов и очередей чистят по расписанию. Таблица, в которую только пишут, растёт линейно и однажды становится самой большой в базе, а пользы от записей годичной давности обычно нет.
Имя таблицы стоит начинать с имени своего собственного решения. Тогда в базе видно, чьи это данные, а случайное совпадение имён с чужим модулем не приводит к разбору посреди рабочего дня.
Своя таблица не даёт интерфейса правки и прав доступа. Если данные нужно показывать и править людям, инфоблок или highload-блок обойдутся дешевле, чем собственная страница в административной части.
Типичные проблемы
На новом стенде таблицы нет.
Её создавали руками, а не классом установки своего модуля. Установка на чистой площадке о такой таблице попросту ничего не знает.
Запросы к таблице стали медленными.
Нет индекса по полю, которое участвует в отборе. Описание сущности индексы не создаёт, их добавляют отдельным запросом.
Ошибка о неизвестном поле после обновления.
Поле добавили в описание сущности, но не добавили в саму таблицу базы. Менять структуру нужно сразу в обоих местах.
Таблица разрослась до миллионов строк.
В неё только пишут и никогда ничего не чистят. Журналам и очередям задают разумный срок хранения записей.
Данные некому править.
У своей таблицы нет ни интерфейса, ни прав доступа. Для данных, которые ведут люди, берут инфоблок или highload-блок.
Частые вопросы
Чем это лучше highload-блока?
Своя таблица даёт полный контроль над структурой и индексами. Highload-блок даёт интерфейс и права из коробки.
Можно ли связать свою таблицу с элементами инфоблока?
Да, полем со ссылкой и описанием связи в сущности. Тогда выборка соберёт данные одним запросом.
Где хранить класс сущности?
В своём модуле, в каталоге классов. Тогда автозагрузка находит его на любом запросе.
Нужны ли миграции как отдельный механизм?
На проекте с несколькими стендами - да, иначе структура расходится. Скрипты обновления модуля закрывают ту же задачу.
Смежное
-
Свои таблицы на ORM - оглавление подтемы
-
Где хранить данные: инфоблок, highload-блок или своя таблица - когда своя таблица действительно нужна
-
Объекты ORM: выборка объектами, ленивая загрузка, сохранение - второй стиль работы с той же сущностью
-
Код, совместимый с PostgreSQL: кавычки, функции, индексы - что править перед сменой базы
-
Выборка ORM возвращает не то: разбор причин - когда выборка возвращает не то
-
Свои номера документов и заявок: шаблон, счётчики, уникальность - как выдавать записям читаемые номера
-
Ядро D7: ORM - устройство слоя данных
-
Свой модуль: структура, установка, автозагрузка - где создаётся таблица
-
Highload-блоки - готовое хранилище со своим интерфейсом
-
Переезд с самописной таблицы на инфоблоки - когда своё хранилище пора менять на инфоблок
-
Illegal mix of collations: разные кодировки таблиц и соединения - кодировка своей таблицы при создании
-
Внешняя база данных: второе соединение, запросы, ORM - та же сущность на чужом соединении
-
База данных: продвинутое - индексы и планы запросов
-
Highload-блок на практике: создание, поля, привязка к свойству - то же хранилище, но с интерфейсом
-
Своя страница в админке: список, форма, пункт меню - интерфейс к своей таблице
-
Отчёты по заказам: агрегаты, группировка, выгрузка - где хранят посчитанные отчёты
-
Связи между таблицами ORM: ссылки, выборка, удаление - когда таблиц становится две
-
Транзакции при записи: откат, границы, что не откатится - когда запись не одна
-
Своя сущность от таблицы до админки: слои, права, интерфейс - задача целиком, а не по слоям
-
История изменений своей таблицы: версии, автор, откат - как хранить правки этих данных