База данных · PPA

Миграции

Winter умеет создавать схему БД из атрибутов сущностей: команда db migrate сканирует репозитории, читает #[Table] и атрибуты колонок и генерирует CREATE TABLE, индексы и внешние ключи. Повторный запуск безопасен.

Команда call db migrateОпт-ин #[Migratable] + #[Table]Режим forward-only, идемпотентно

Что такое миграции и зачем

Миграция здесь — генерация DDL (структуры БД) из кода.

Проблема. Держать SQL-схему отдельно от сущностей — двойная работа: добавили поле в класс, не забудь ALTER TABLE. Схема и код расходятся, а поднять БД с нуля на новой машине — ручной труд.

Решение. Опишите структуру один раз атрибутами сущности (см. Сущности), а db migrate создаст таблицы из них. Об этом и раздел.

Это не полноценная система миграций

db migrateforward-only генератор: создаёт то, чего нет. Он не делает diff, DROP, изменение колонок, миграцию данных и не ведёт таблицу версий (см. Ограничения). Обычно применяется на первом деплое и в dev; эволюцию схемы в проде ведут отдельным инструментом.

Три условия опт-ина

Чтобы репозиторий попал в миграцию, нужны все три — иначе он молча пропускается:

  1. Репозиторий обнаружим — файл под Kernel::$pathRoot (или корнем плагина).
  2. У сущности есть #[Table] — маркер таблицы.
  3. У конфига есть #[Migratable] — иначе конфиг и его репозитории не сканируются.
php
#[Migratable]                                    // (3) конфиг включён в миграцию
class MainDbConfig extends DbConfig { /* ... */ }

#[Table]                                         // (2) сущность миграбельна
class User { /* поля с атрибутами колонок */ }

class UserRepository extends Repository          // (1) лежит под pathRoot
{
  protected string $dbConfigClassName = MainDbConfig::class;
  protected string $entityClassName   = User::class;
  public static string $table         = 'users';
}

Запуск

bash
php call db ping       # проверить подключение
php call db sql        # показать сгенерированный SQL (без выполнения)
php call db migrate    # выполнить миграцию

db sql — безопасный предпросмотр: печатает DDL, ничего не меняя. db migrate выполняет. Полный набор флагов — в CLI → db.

Приоритет и расширения

Атрибуты на классе конфига:

  • #[Migratable(priority)] — порядок между конфигами: HighNormalLow (меньше — раньше). Полезно, когда одни таблицы ссылаются на другие.
  • #[Extension('name', ...)] — расширение PostgreSQL, повторяемо. Ставится как CREATE EXTENSION IF NOT EXISTS до создания таблиц.
php
use Flytachi\Winter\K2\Ppa\Mapping\Attributes\Config\Migratable;
use Flytachi\Winter\K2\Ppa\Mapping\Attributes\Config\Extension;
use Flytachi\Winter\K2\Ppa\Mapping\Constants\MigratablePriority;

#[Migratable(MigratablePriority::High)]   // раньше остальных
#[Extension('pgcrypto')]                  // для gen_random_uuid()
class AuthDbConfig extends DbConfig { /* ... */ }

Порядок выполнения

Внутри одного конфига DDL эмитится в фиксированном порядке — по цепочке зависимостей:

text
1. EXTENSIONS    (только pgsql)
2. SCHEMAS       (только pgsql — CREATE SCHEMA)
3. TABLES        (CREATE TABLE, включая первичные ключи)
4. INDEXES       (CREATE INDEX)
5. CONSTRAINTS   (ALTER TABLE ADD CONSTRAINT — FK, CHECK)

Расширения первыми, потому что DEFAULT-выражения таблиц могут ссылаться на их функции (gen_random_uuid()); схемы до таблиц; индексы и ограничения после — они ссылаются на колонки.

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

Повторный db migrate безопасен: каждый оператор либо использует IF NOT EXISTS, либо его ошибка «объект уже существует» перехватывается и помечается EXIST (жёлтым), а не FAILED (красным). Любой другой SQLSTATE → FAILED (с текстом ошибки при DEBUG=true).

Выбор фаз флагами

bash
php call db migrate          # все фазы (по умолчанию -e -s -t -i -c)
php call db migrate -t -i     # только таблицы + индексы
php call db migrate -e        # только расширения (pgsql)

Ограничения

Границы инструмента, чтобы не было сюрпризов в проде:

  • Нет diff / drift-детекции — только создаёт отсутствующее, не замечает изменённых/удалённых колонок.
  • Нет DROP — ни таблиц, ни колонок, ни ограничений.
  • Нет транзакций — операторы выполняются независимо.
  • Нет миграции данных — только DDL, сидинг отдельно.
  • Нет таблицы версий — каждый запуск прогоняет все операторы (идемпотентно).

Для эволюции схемы в проде используйте выделенный инструмент (Phinx, Liquibase), оставив соответствующие сущности без #[Table] (или конфиг без #[Migratable]).

Дальше