CakePHP3のschema管理(Migrations)について

CakePHP3では、DBスキーマの更新管理にPhinxを使ったMigrationsというプラグインを使います。(Migrationsはデフォルトで入っています。)

使い方をざっくり説明します。

# 既存のテーブルからマイグレーションファイルを作る

1から書いてもいいのですがけっこう大変なので、最初はテーブルを作成してからマイグレーションファイルをbakeするのが簡単です。

```
bin/cake bake migration_snapshot Initial
```

`app/config/Migrations/20151204000000_initial.php` のようなマイグレーションファイルができます。

# コマンド操作

`app/config/Migrations/`以下に作成したマイグレーションファイルを元にDBに適用します。

## 適用
未適用のファイルを適用します。

```
bin/cake migrations migrate
```

## ロールバック
適用したものを戻します。

```
bin/cake migrations rollback
```

## 適用状況を確認
どれが適用済みでどれが未適用かなどのステータスを確認できます。

```
bin/cake migrations status
```

↓こんな感じのものが出力されます。

```
 Status  Migration ID    Migration Name 
-----------------------------------------
     up  20150522085101  Initial
     up  20150526022603  RenameColumn
   down  20150527003127  AddPriceToProducts
```

upが適用済みでdownが未適用です。

# マイグレーションファイル

bakeしてできたファイルを見たらなんとなくわかると思いますが、`up()`、`down()`、`change()`というメソッドの中に、追加・削除・変更の定義を書きます。
`up()`に適用したいもの(migrateで実行したいもの)を書き、`down()`には`up()`に書いたものをロールバックする時に実行するべきものを書きます。
例えば、`up()`にcreate tableを書いたら、`down()`にdrop tableを書きます。

```php
class Initial extends AbstractMigration
{
    public function up()
    {
        $table = $this->table('users');
        $table
            ->addColumn('name', 'string', [
                'default' => null,
                'limit' => 50,
                'null' => false,
            ])
            ->addColumn('profile', 'text', [
                'default' => null,
                'limit' => null,
                'null' => true,
            ])
            ->addColumn('created', 'datetime', [
                'default' => null,
                'limit' => null,
                'null' => true,
            ])
            ->addColumn('modified', 'datetime', [
                'default' => null,
                'limit' => null,
                'null' => true,
            ])
            ->create();
    }

    public function down()
    {
        $this->dropTable('users');
    }
}
```

`change()`も`up()`と同じように適用したいものを書きますが、`change()`に書くと、`down()`は自動的に推測してくれます。
なので、自動的に推測できるものしか`change()`には書けません。
例えばaddColumnは自動的にremoveColumnが推測できるので書けますが、removeColumnはaddColumnが推測できない(カラムの定義がわからない)ので書けません。(書けなくはないけど、ロールバックできなくなるんだと思います。)
また、`save()`は使えず、`create()`と`update()`を使う必要があります。

```php:OK
class AddBirthdayToUsers extends AbstractMigration
{
    public function change()
        $table = $this->table('users');
        $table
            ->addColumn('birthday', 'date', [
                'default' => null,
                'limit' => null,
                'null' => true,
            ])
            ->update();
    }
}
```

```php:NG
class RemoveProfileFromUsers extends AbstractMigration
{
    public function change()
    {
        $table = $this->table('users');
        $table->removeColumn('profile');
        $table->update();
    }
}
```

また、`change()`を書くと`up()`と`down()`は無視されるので、`change()`だけのファイルか`up()`と`down()`のファイルかのどちらかにする必要があります。

* * *

マイグレーションファイル書くのはちょっとめんどうな気もしますが、bakeでも作成できますし、バージョン管理できるのと、開発で変更したものを本番に適用する時も楽にできます。

さらに詳細は、
[Cookbook 3.x - Migrations](http://book.cakephp.org/3.0/ja/migrations.html) (いつのまにか翻訳されてる！)
と
[Phinx - Writing Migrations](http://phinx.readthedocs.org/en/latest/migrations.html)
を見てください。