← Назад к справке

Модули

Миграции таблиц

Рабочая справка по созданию и обновлению таблиц локального модуля 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();

Источники