26Миграции
Версионирование схемы БД — создание миграции, up/down и safeUp/safeDown, генерация по шаблонам (create_table, add_column, junction), методы Migration, команды migrate/up|down|redo|fresh|mark|history|new, несколько БД и пространства имён.
Миграция — PHP-класс с методами «применить» и «откатить», который меняет структуру или данные БД. Файлы лежат в репозитории, применяются командой ./yii migrate, а таблица migration помнит, что уже выполнено. Так схема эволюционирует синхронно с кодом на всех машинах команды и серверах.
Создать миграцию#
./yii migrate/create create_news_table
# → migrations/m150101_185401_create_news_table.phpИмя файла m<YYMMDD_HHMMSS>_<имя> — порядок применения определяется временной меткой.
use yii\db\Migration;
class m150101_185401_create_news_table extends Migration
{
public function up()
{
$this->createTable('news', [
'id' => $this->primaryKey(),
'title' => $this->string()->notNull(),
'content' => $this->text(),
'created_at' => $this->integer()->notNull()->defaultValue(0),
]);
$this->createIndex('idx-news-title', 'news', 'title');
}
public function down()
{
$this->dropTable('news');
// return false; // если откат невозможен
}
}up/down или safeUp/safeDown#
safeUp()/safeDown() оборачивают всё в транзакцию: исключение — и всё откатывается. Используйте их по умолчанию; up()/down() — только если СУБД не умеет транзакционный DDL (MySQL коммитит неявно на каждый CREATE TABLE, так что там разницы почти нет).
Строитель типов столбцов#
Вместо строк 'string NOT NULL' — переносимые методы yii\db\SchemaBuilderTrait:
'id' => $this->primaryKey(), // pk: INT AUTO_INCREMENT PRIMARY KEY (bigPrimaryKey())
'title' => $this->string(255)->notNull()->unique(),
'content' => $this->text(),
'price' => $this->decimal(10, 2)->defaultValue(0),
'is_active' => $this->boolean()->defaultValue(true),
'created_at' => $this->timestamp()->defaultExpression('CURRENT_TIMESTAMP'),
'status' => $this->smallInteger()->notNull()->defaultValue(1)->comment('статус'),
'user_id' => $this->integer()->notNull()->after('id'), // after()/first() — только MySQL
// также: char, float, double, money, date, time, dateTime, binary, tinyInteger, json (2.0.14)Строковые типы тоже работают: 'string NOT NULL', 'pk', 'bigpk', 'text' — конвертируются под СУБД.
Методы Migration#
createTable($table, $columns, $options) / dropTable / renameTable / truncateTable$options — например 'ENGINE=InnoDB' для MySQLaddColumn / dropColumn / renameColumn / alterColumnaddPrimaryKey / dropPrimaryKeyaddForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update) / dropForeignKey'CASCADE', 'SET NULL', 'RESTRICT')createIndex($name, $table, $columns, $unique = false) / dropIndexaddCommentOnColumn / addCommentOnTable / dropCommentFrom…insert / batchInsert / update / delete / upsertexecute($sql, $params)Все они выводят время выполнения и работают через $this->db. Обычные Yii::$app->db->createCommand() тоже доступны, но прогресса не покажут.
$this->createTable('{{%post}}', [...]); // {{%…}} — с префиксом таблиц
$this->addForeignKey('fk-post-author_id', '{{%post}}', 'author_id', '{{%user}}', 'id', 'CASCADE');
$this->insert('{{%category}}', ['name' => 'Общее']);Генерация по имени#
Команда migrate/create понимает шаблоны имён и заполняет тело за вас:
./yii migrate/create create_post_table # createTable + dropTable
./yii migrate/create create_post_table --fields="title:string(12):notNull:unique,body:text"
./yii migrate/create create_post_table \
--fields="author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey"
./yii migrate/create drop_post_table --fields="title:string(12):notNull" # dropTable + createTable в down
./yii migrate/create add_position_column_to_post_table --fields="position:integer"
./yii migrate/create drop_position_column_from_post_table --fields="position:integer"
./yii migrate/create create_junction_table_for_post_and_tag_tables # post_tag(post_id, tag_id) + PK + FKПоле id:primaryKey добавляется автоматически, если не задано. foreignKey(user) создаст индекс и внешний ключ на user.id. Формат --fields: имя:тип(args):модификатор(args):….
Применить и откатить#
./yii migrate # применить все новые (спросит подтверждение)
./yii migrate 3 # только 3 следующие
# до указанной (или m150101_185401_create_news_table, или timestamp, или 'yyyy-mm-dd hh:mm:ss')
./yii migrate/to 150101_185401
./yii migrate/down # откатить последнюю
./yii migrate/down 3 # три последние; migrate/down all — все
./yii migrate/redo # откатить и снова применить последнюю (redo 3, redo all)
./yii migrate/fresh # ⚠ удалить все таблицы и применить всё с нуля — только dev
./yii migrate/history # что применено (history 5 / history all)
./yii migrate/new # что ещё не применено
./yii migrate/mark 150101_185401 # пометить как применённую без выполнения (mark m000000_000000_base — сбросить всё)
./yii migrate --interactive=0 # без вопросов — для CIНастройка#
./yii migrate --migrationPath=@app/modules/forum/migrations
./yii migrate --migrationTable=my_migrations
./yii migrate --db=db2 # другое соединение
./yii migrate --migrationPath=@yii/rbac/migrations # миграции расширений (RBAC, i18n, queue…)Постоянно — в консольной конфигурации:
'controllerMap' => [
'migrate' => [
'class' => 'yii\console\controllers\MigrateController',
'migrationPath' => null, // отключить старый механизм
'migrationNamespaces' => [ // миграции с пространствами имён (2.0.10)
'app\migrations',
'some\extension\migrations',
],
// 'migrationPath' => ['@app/migrations', '@app/modules/forum/migrations'], // или несколько путей
],
],Именованная миграция создаётся ./yii migrate/create app\\migrations\\CreateUserTable → класс M150101185401CreateUserTable в папке псевдонима @app/migrations.
Несколько БД#
Либо своя команда с --db, либо переопределить getDb() в миграции. Разложить по папкам (migrations/db1, migrations/db2) и запускать с разными --migrationPath.
Быстрый рецепт#
./yii migrate/create create_post_table --fields="title:string:notNull,body:text,author_id:integer:notNull:foreignKey(user)"- Проверить сгенерированный файл, поправить типы, добавить индексы.
./yii migrateлокально; закоммитить файл миграции.- На сервере после деплоя:
./yii migrate --interactive=0. - Ошибка после деплоя —
./yii migrate/downвернёт схему назад.
safeUp() выполняется внутри транзакции — при исключении всё откатывается (если СУБД поддерживает транзакционный DDL).
Ничего: применённые миграции помнит таблица migration, выполняются только новые.
./yii migrate/create create_post_table --fields="author_id:integer:notNull:foreignKey(user)".
./yii migrate --migrationPath=@yii/rbac/migrations или добавить путь/пространство имён в конфигурацию controllerMap.
Когда откат невозможен (например, данные уничтожены); тогда migrate/down остановится с сообщением.