Как писать миграции в yii framework 2.0

Как писать миграции в yii framework 2.0
Photo by Emile Perron / Unsplash

Наиболее уместный и удобный вариант написания миграций для фреймворка Yii 2.0.

В данной статье вы увидите одну из общепринятых практик написания миграций для популярного фреймворка Yii. Многие проекты блога catine используют данный подход, так как он считается общепринятым в нашей команде разработчиков. Надеюсь, вы используете Linux, как основную операционную систему для разработки. Если же вы используете Windows, то советую вам обратиться к Vagrant и начать использовать unix системы при разработке.

Давайте договоримся, что у нас есть следующая схема данных, которую необходимо реализовать:

Таблица articles – таблица, которая уже существует в бд, в миграции будем на нее только ссылаться. Эта сущность обозначает новости на сайте, описывается следующим набором полей:

  • id – идентификатор, первичный ключ
  • title – название новости, текстовое поле
  • content – содержание новости, текстовое поле

Таблица tasks – задачи для новостей, сущность будет создаваться в миграции. Имеет следующий набор полей:

  • id – идентификатор, первичный ключ
  • title – текст задачи, текстовое поле
  • sort – поле для сортировки, при создании равно нулю

Таблица articles_has_tasks – таблица пересечения задач и новостей, так как у нас связь между таблицами N:M многие ко многим. Имеет следующий набор полей:

  • task_id – ссылка на задачу
  • article_id – ссылка на новость

Для создания миграции необходимо перейти в рабочий каталог с установленным yii framework`ом и выполнить команду:

php yii migrate/create название_миграции

В нашем случае необходимо будет выполнить команду  php yii migrate/create create_task_table и в ответ на вопрос ответь yes.

И после того, как вы увидели заветную надпись New migration created successfully., можно приступить к написанию кода миграции.

Как учит писать нас документация по yii 2.0 миграции:

<?php

use yii\db\Schema;
use yii\db\Migration;

class m160511_031901_create_task_table extends Migration
{
    public function safeUp()
    {
        $this->createTable('tasks', [
            'id'            => Schema::TYPE_INTEGER . ' NOT NULL',
            'lang' =>       'CHAR(2) NOT NULL DEFAULT "ru" COLLATE utf8_unicode_ci',
            'title' =>      Schema::TYPE_STRING . ' NOT NULL',
            'sort' =>       Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0',
            'PRIMARY KEY (`id`, `lang`)'
        ], 'CHARSET=utf8 COLLATE=utf8_general_ci');

		$this->createTable('articles_has_tasks', [
			'article_id' =>  Schema::TYPE_INTEGER . ' NOT NULL',
			'task_id' =>        Schema::TYPE_INTEGER . ' NOT NULL',
		], 'CHARSET=utf8 COLLATE=utf8_general_ci');

		$this->addPrimaryKey('articles_has_tasks' . '_pk', 'articles_has_tasks', ['article_id', 'task_id']);

		$this->addForeignKey('task_' . 'articles_has_tasks' . '_fk', 'articles_has_tasks', 'task_id', 'tasks', 'id');

		$this->addForeignKey('articles' . '_fk', 'articles_has_tasks', 'article_id', 'articles', 'id');
    }

    public function safeDown()
    {
        $this->dropTable('articles_has_tasks');
        $this->dropTable('tasks');
    }
}

Данная миграция уже будет работать, применяться (команда php yii migrate 1) и откатываться без ошибок (команда php yii migrate/down 1), но не всегда понятно какой sql запрос будет сформирован, поэтому советую вам использовать другой подход написания миграции, более приближенный к sql запросам.

Данную миграцию можно было бы написать следующим образом:

<?php

use yii\db\Schema;
use yii\db\Migration;

class m160511_031901_create_task_table extends Migration
{
    public function safeUp()
    {
        $this->createTable('tasks', [
            'id'    => 'int(11) NOT NULL',
            'lang'  => 'CHAR(2) NOT NULL DEFAULT "ru" COLLATE utf8_unicode_ci',
            'title' => 'char(255) NOT NULL COLLATE utf8_unicode_ci',
            'sort'  => 'int(11) NOT NULL DEFAULT 0',
            'PRIMARY KEY (`id`, `lang`)'
        ], 'CHARSET=utf8 COLLATE=utf8_general_ci');

        $this->createTable('articles_has_tasks', [
            'article_id' => 'int(11) NOT NULL',
            'task_id'    => 'int(11) NOT NULL',
            'PRIMARY KEY (`article_id`, `task_id`)',
            'FOREIGN KEY (`article_id`) REFERENCES `articles`(`id`)',
            'FOREIGN KEY (`task_id`) REFERENCES `tasks`(`id`)'
        ], 'CHARSET=utf8 COLLATE=utf8_general_ci');
    }

    public function safeDown()
    {
        $this->dropTable('articles_has_tasks');
        $this->dropTable('tasks');
    }
}

Данный подход дает позволяет более точно определить какой запрос будет сформирован при выполнении миграции, что увеличивает понимание в команде разработчиков. Такой код легче всего читать и писать, так как он максимально приближен к sql.

А как быть, если я хочу провести сразу все миграции?

Чтобы провести все миграции, которые когда-либо были созданы, но не были проведены, можно командой: php yii migrate
Вам автоматически будет предложено провести все миграции, которые не были выполнены на данной базе. Информация об этом хранится в самой бд – таблица migration.

А вот отменить все миграции можно только через команду: php yii migratedown N, где вместо N необходимо указать количество миграций, которые необходимо отменить. Если ввести без указания N, то будет предложено откатить лишь последнюю миграцию.