Модули
Миграции таблиц
Рабочая справка по созданию и обновлению таблиц локального модуля Bitrix через ORM и простые миграции.
Для простого модуля достаточно install.sql. Но если модуль развивается, удобнее
делать миграции: маленькие PHP-классы, которые применяются по порядку и не теряют данные.
Общее понимание
Таблицы модуля можно создавать SQL-файлами или через ORM.
install.sql или миграции
| Подход | Когда подходит |
|---|---|
install.sql | Небольшой модуль, таблицы создаются один раз при установке. |
| PHP-миграции | Модуль развивается, нужно добавлять поля и индексы на уже работающем портале. |
ORM createDbTable() | Таблица полностью описана в D7 Table-классе. |
ORM-таблица модуля
Для своих таблиц удобно делать D7 Table-класс. Тогда таблицу можно использовать через
getList(), add(), update(), delete().
Создание таблицы
Минимальный пример своей ORM-таблицы и её создания при установке модуля.
Table-класс
<?php
namespace test\Example\Model;
use Bitrix\Main\Entity;
use Bitrix\Main\ORM\Fields;
class ExampleTable extends Entity\DataManager
{
/**
* Возвращает имя таблицы.
*/
public static function getTableName(): string
{
return 'b_test_example_item';
}
/**
* Возвращает описание полей таблицы.
*/
public static function getMap(): array
{
return [
new Fields\IntegerField('ID', [
'primary' => true,
'autocomplete' => true,
]),
new Fields\StringField('TITLE', [
'required' => true,
'size' => 255,
]),
new Fields\IntegerField('CRM_ENTITY_ID'),
new Fields\DatetimeField('CREATED_AT'),
];
}
} Создать таблицу через ORM
<?php
use Bitrix\Main\Application;
use test\Example\Model\ExampleTable;
/**
* Создаёт таблицу, если она ещё не существует.
*/
function createExampleTable(): void
{
$connection = Application::getConnection();
$table_name = ExampleTable::getTableName();
if ($connection->isTableExists($table_name)) {
return;
}
ExampleTable::getEntity()->createDbTable();
}
createExampleTable();
Такой код можно вызвать из installDb() установщика модуля.
Удалить таблицу
<?php
use Bitrix\Main\Application;
use test\Example\Model\ExampleTable;
/**
* Удаляет таблицу, если она существует.
*/
function dropExampleTable(): void
{
$connection = Application::getConnection();
$table_name = ExampleTable::getTableName();
if (!$connection->isTableExists($table_name)) {
return;
}
$connection->dropTable($table_name);
}
dropExampleTable(); Миграции версий
Простой подход: хранить список применённых миграций в отдельной таблице.
Хранить применённые миграции
<?php
use Bitrix\Main\Application;
/**
* Создаёт таблицу применённых миграций.
*/
function createMigrationTable(): void
{
$connection = Application::getConnection();
if ($connection->isTableExists('b_test_example_migration')) {
return;
}
$connection->queryExecute("
CREATE TABLE b_test_example_migration (
ID int not null auto_increment,
MIGRATION varchar(255) not null,
APPLIED_AT datetime not null,
primary key (ID),
unique key ux_migration (MIGRATION)
)
");
}
createMigrationTable(); Применить миграции
Ниже упрощённый пример. Для сложного проекта лучше сделать отдельный класс
MigrationService.
<?php
use Bitrix\Main\Application;
use Bitrix\Main\Type\DateTime;
/**
* Получает список применённых миграций.
*/
function fetchAppliedMigrations(): array
{
$connection = Application::getConnection();
$rows = [];
$result = $connection->query("
SELECT MIGRATION
FROM b_test_example_migration
");
while ($row = $result->fetch()) {
$rows[] = $row['MIGRATION'];
}
return $rows;
}
/**
* Отмечает миграцию как применённую.
*/
function saveAppliedMigration(string $migration): void
{
$connection = Application::getConnection();
$sql_helper = $connection->getSqlHelper();
$safe_migration = $sql_helper->forSql($migration);
$connection->queryExecute("
INSERT INTO b_test_example_migration (MIGRATION, APPLIED_AT)
VALUES ('{$safe_migration}', NOW())
");
}
/**
* Добавляет поле к таблице, если его ещё нет.
*/
function addExampleStatusColumn(): void
{
$connection = Application::getConnection();
if ($connection->getTableField('b_test_example_item', 'STATUS')) {
return;
}
$connection->queryExecute("
ALTER TABLE b_test_example_item
ADD STATUS varchar(50) null
");
}
/**
* Применяет миграции модуля.
*/
function applyModuleMigrations(): void
{
createMigrationTable();
$applied_migrations = fetchAppliedMigrations();
$migrations = [
'202604260001_add_status_column' => 'addExampleStatusColumn',
];
foreach ($migrations as $migration => $callback) {
if (in_array($migration, $applied_migrations, true)) {
continue;
}
$callback();
saveAppliedMigration($migration);
}
}
applyModuleMigrations();