Files
ave-cms/help/modules/migrations.md
T
2026-07-27 12:58:44 +03:00

7.1 KiB
Raw Permalink Blame History

Миграции модуля

К разделу «Модули»

Управляемый модуль создаёт таблицы и исходные данные при явной установке или обновлении через панель управления. Production не требует CLI.

Объявление

'migrations' => array(
    array(
        'id' => '001_create_notes',
        'file' => 'migrations/001_create_notes.sql',
    ),
    array(
        'id' => '002_add_note_color',
        'file' => 'migrations/002_add_note_color.sql',
    ),
	array(
		'id' => '003_normalize_note_settings',
		'file' => 'migrations/003_normalize_note_settings.php',
	),
),

id уникален внутри модуля и записывается в системный ledger. Уже выполненный ID второй раз не применяется. Поэтому опубликованную миграцию не редактируют: любое следующее изменение получает новый файл и новый ID.

Префиксы таблиц

В SQL нельзя писать префикс конкретного проекта. Используйте плейсхолдеры:

Плейсхолдер Назначение
{{prefix}} Основной префикс текущей установки.
{{content_prefix}} Контентные таблицы.
{{catalog_prefix}} Таблицы каталога.
{{module_prefix}} Таблицы модулей.
{{basket_prefix}} Таблицы корзины.
{{contacts_prefix}} Таблицы контактных форм.
{{system_prefix}} Системные таблицы.
CREATE TABLE IF NOT EXISTS `{{prefix}}_admin_notes` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `user_id` INT UNSIGNED NOT NULL,
  `title` VARCHAR(200) NOT NULL DEFAULT '',
  `content` TEXT NULL,
  `created_at` INT UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Идемпотентность

Ledger защищает от повторного запуска после успеха, но миграция всё равно должна быть насколько возможно идемпотентной. Используйте IF NOT EXISTS, ON DUPLICATE KEY UPDATE и проверки в lifecycle-handler, когда SQL зависит от текущей схемы. Для ADD COLUMN IF NOT EXISTS и ADD INDEX IF NOT EXISTS мигратор сам проверяет information_schema и отправляет переносимый синтаксис, поэтому такие миграции работают и на старых версиях MySQL/MariaDB.

Не выполняйте в одной миграции необратимое удаление и долгую массовую обработку без возможности продолжения. Для больших объёмов предоставьте защищённую пакетную web-операцию с прогрессом.

PHP-миграции данных

Если изменение требует разобрать JSON или выполнить условное преобразование существующих строк, используйте PHP вместо функций конкретной версии MySQL:

<?php

defined('BASEPATH') || die('Direct access to this location is not allowed.');

return function (array $context) {
	$rows = DB::query('SELECT id, settings FROM my_table')->getAll() ?: array();

	foreach ($rows as $row) {
		$settings = \App\Helpers\Json::toArray($row['settings']);
		$settings['enabled'] = true;
		DB::Update('my_table', array(
			'settings' => \App\Helpers\Json::encode($settings),
		), 'id = %i', (int) $row['id']);
	}

	return count($rows) + 1;
};

Файл должен вернуть callable. Он получает module и migration в $context и возвращает неотрицательное количество DB-операций. Мигратор выполняет callback и запись в ledger в одной транзакции, поэтому не вызывайте внутри commit() или rollback() самостоятельно.

Install и update handlers

SQL подходит не для каждой операции. Descriptor может дополнительно объявить callable:

'lifecycle' => array(
    'managed' => true,
    'install' => array(Installer::class, 'install'),
    'update' => array(Installer::class, 'update'),
    'uninstall' => array(Installer::class, 'uninstall'),
),

Handler нужен для безопасного преобразования данных, создания каталогов или другой операции, которую нельзя выразить переносимым SQL. Он обязан бросить исключение при ошибке: менеджер не должен отмечать операцию успешной.

Деинсталляция

Простой вариант:

'lifecycle' => array(
    'managed' => true,
    'uninstall' => array('migrations/uninstall.sql'),
),
DROP TABLE IF EXISTS `{{prefix}}_admin_notes`;

Права модуля синхронизируются менеджером и удаляются штатной деинсталляцией. Собственный uninstall-файл обязан удалить принадлежащие модулю таблицы и данные. Общие настройки и локальные секреты удаляет менеджер модулей. Для сохранения данных используется выключение, а не деинсталляция.

reinstall сначала полностью удаляет данные через uninstall, а затем выполняет чистую установку. Не предлагайте эту кнопку как безопасное исправление без backup.

Проверка миграции

  • SQL не содержит префикс проекта;
  • таблицы создаются в InnoDB и нужной кодировке;
  • у новых таблиц есть первичные ключи и индексы под реальные запросы;
  • новая версия не меняет уже опубликованный migration ID;
  • уже применённый ID не выполняется повторно; новая официальная версия файла обновляет checksum ledger без повторного изменения данных;
  • ошибка останавливает установку и видна администратору;
  • uninstall проверен отдельно на тестовой копии;
  • перед необратимым обновлением сделан backup.

Состояния и блокировки операций описаны в Операциях модуля.