> For the complete documentation index, see [llms.txt](https://docs.johncms.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.johncms.com/10.0/baza-dannykh/migracii.md).

# Миграции базы данных

Схема базы данных JohnCMS описана миграциями — файлами, каждый из которых вносит в базу одно изменение. Установка сайта с нуля и обновление существующего идут одним путём: применяются одни и те же миграции. Второго описания таблиц в системе нет.

Система помнит, через что база уже прошла: применённые миграции записываются в таблицу `migrations`. Благодаря этому повторный запуск ничего не ломает, а восстановленный дамп приносит с собой ровно то состояние, которому соответствует.

## Команды

```bash
php system/bin/console migrate            # применить всё, что ещё не применено
php system/bin/console migrate --dry-run  # показать, что будет применено, и ничего не менять
php system/bin/console migrate:status     # что применено, что ждёт своей очереди
```

`migrate` безопасно запускать повторно: уже применённое пропускается.

### Обновление без консоли

Если доступа к консоли нет, откройте в админ-панели **Обслуживание** (`/admin/maintenance`) и запустите задачу «Update the database». Она выполняется в фоне планировщиком, поэтому не упирается в таймаут веб-сервера. Для этого должна быть настроена cron-задача планировщика:

```bash
php /путь/к/сайту/system/bin/console schedule:run --no-interaction
```

Пока в базе не применено хотя бы одно изменение, админ-панель говорит об этом на каждой странице и предлагает кнопку.

### Откат

```bash
php system/bin/console migrate:rollback
```

Откат — инструмент разработки, а не эксплуатации: он отменяет шаг, через который данные уже прошли, и то, что этот шаг принёс, теряется. Большинство миграций отменять отказываются. На сайте, у которого выключен режим отладки, команда требует явного `--force`.

## Если миграция упала

Прогон останавливается на упавшей миграции, и она **не** записывается в журнал — значит, следующий запуск начнётся с неё. Но MySQL не откатывает изменения структуры таблиц, поэтому часть своей работы миграция могла успеть выполнить; об этом прямо сказано в сообщении об ошибке.

Порядок действий: прочитать сообщение, устранить причину (чаще всего это нехватка прав у пользователя БД или уже существующая таблица), запустить `migrate` снова.

**Перед обновлением сайта делайте резервную копию базы данных.** Это единственный способ вернуться назад, если что-то пойдёт не так.

## Для разработчиков модулей

Миграции модуля лежат в `modules/<вендор>/<модуль>/migrations/`, миграции ядра — в `system/migrations/`. Источник миграций называется по алиасу модуля (`forum`), а не по ключу (`johncms/forum`). Создаются командой:

```bash
php system/bin/console make:migration forum add_slug_to_sections --table=forum_sections
php system/bin/console make:migration forum create_widgets_table --table=widgets --create
```

Файл возвращает анонимный класс:

```php
<?php

declare(strict_types=1);

use Johncms\Database\Migrations\Migration;
use Johncms\Database\Schema\TableDefinition;

return new class extends Migration {
    public function up(): void
    {
        $this->schema->create('forum_sections', static function (TableDefinition $table): void {
            $table->increments('id');
            $table->integer('parent')->unsigned()->default(0)->index();
            $table->string('slug')->unique();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        $this->schema->dropIfExists('forum_sections');
    }
};
```

Миграции доступны ровно две вещи: `$this->schema` для изменения структуры и `$this->db` для запросов.

### Правила

* **Никаких моделей, репозиториев, сервисов и настроек.** Миграция — исторический документ: написанная сегодня, она должна без изменений отработать через несколько лет на чужом сайте, где код вокруг давно изменился. Данные переносите запросами через `$this->db`.
* **Выпущенную миграцию не правят.** Ошибку исправляют новой миграцией поверх. `migrate:status` предупредит, если файл изменили после применения.
* **Миграция не трогает таблицы чужого модуля.**
* **Только то, что есть в `TableDefinition`.** Приёмы, специфичные для одной СУБД, не переживут смены слоя работы с базой.

`down()` необязателен: молчание — это отказ откатываться, а не потеря данных.

### Имя файла

`2026_09_01_120000_add_slug_to_sections.php` — метка времени и описание. В журнале миграция опознаётся по **источнику и версии**, а не по имени файла: два модуля могут попасть в одну минуту, а описание автор вправе поправить. Имя источника — это имя каталога модуля, и переименовывать его после того, как миграции где-то применились, нельзя.
