データベースマイグレーション
============================

> Note|注意: この節はまだ執筆中です。

ソースコードと同じように、データベースの構造も、データベース駆動型のアプリケーションが開発され保守されるにともなって徐々に発展していきます。
例えば、開発中に新しいテーブルが追加されることもあるでしょうし、アプリケーションが実運用に移行した後になって追加のインデックスが必要であることが発見されることもあるでしょう。
このようなデータベースの構造的な変更 (**マイグレーション** と呼ばれます) を追跡記録することが重要であるのは、ソースコードに対する変更がバージョン管理を使って追跡記録されるのと全く同じことです。
ソースコードとデータベースの同期が失われると、バグが発生するか、アプリケーション全体が動かなくなるかします。
こうした理由によって、データベースマイグレーションツールを提供して、データベースマイグレーションの履歴の追跡管理、新しいマイグレーションの適用、また、既存のマイグレーションの取消が出来るようにしています。

下記のステップは、開発中にチームによってデータベースマイグレーションが使用される例を示すものです。

1. Tim が新しいマイグレーション (例えば、新しいテーブルを作成したり、カラムの定義を変更したりなど) を作る。
2. Tim が新しいマイグレーションをソースコントロールシステム (例えば Git や Mercurial) にコミットする。
3. Doug がソースコントロールシステムから自分のレポジトリを更新して新しいマイグレーションを受け取る。
4. Doug がマイグレーションを彼のローカルの開発用データベースに適用し、Tim が行った変更を反映して、自分のデータベースを同期する。

Yii はデータベースマイグレーションを `yii migrate` コマンドラインツールによってサポートします。
このツールは、以下の機能をサポートしています。

* 新しいマイグレーションの作成
* マイグレーションの適用、取消、再適用
* マイグレーションの履歴と新規マイグレーションの閲覧


マイグレーションを作成する
--------------------------

新しいマイグレーションを作成するためには、次のコマンドを実行します。

```
yii migrate/create <name>
```

要求される `name` パラメータには、マイグレーションの非常に短い説明を指定します。
例えば、マイグレーションが *news* という名前のテーブルを作成するものである場合は、コマンドを次のようにして使います。

```
yii migrate/create create_news_table
```

すぐ後で説明するように、マイグレーションでは、この `name` パラメータは PHP のクラス名の一部として使用されます。
したがって、アルファベット、数字、および/または、アンダースコアだけを含まなければなりません。

上記のコマンドは、`m101129_185401_create_news_table.php` という名前の新しいファイルを作成します。
このファイルは `@app/migrations` ディレクトリに作成されます。
初期状態では、このマイグレーションファイルは以下のコードを含んでいます。

```php
class m101129_185401_create_news_table extends \yii\db\Migration
{
    public function up()
    {
    }

    public function down()
    {
        echo "m101129_185401_create_news_table cannot be reverted.\n";
        return false;
    }
}
```

クラス名はファイル名と同じであり、`m<timestamp>_<name>` というパターンに従います。ここで、

* `<timestamp>` は、マイグレーションが作成された (`yymmdd_hhmmss` という書式の) UTC タイムスタンプであり、
* `<name>` は、コマンドの `name` パラメータから取られた文字列です。

クラスの中では、`up()` メソッドが、実際のデータベースマイグレーションを実装するコードを含むべきメソッドです。
言い換えると、`up()` メソッドが実際にデータベースを変更するコードを実行します。
`down()` メソッドは、`up()` によって加えられた変更を取り消すコードを含むことが出来ます。

場合によっては、`down()` がデータベースマイグレーションを取り消すことが出来ないことがあります。
例えば、マイグレーションがテーブルの行やテーブル全体を削除した場合は、そのデータを `down()` メソッドで復旧することは出来ません。
そのような場合には、マイグレーションは不可逆であると呼ばれ、データベースを以前の状態にロールバックすることは出来ません。
マイグレーションが不可逆である場合は、生成された上記のコードのように、`down()` メソッドは `false` を返して、マイグレーションが取り消せないものであることを示します。

例として、新しいテーブルを作成するマイグレーションを示しましょう。

```php

use yii\db\Schema;

class m101129_185401_create_news_table extends \yii\db\Migration
{
    public function up()
    {
        $this->createTable('news', [
            'id' => 'pk',
            'title' => Schema::TYPE_STRING . ' NOT NULL',
            'content' => Schema::TYPE_TEXT,
        ]);
    }

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

}
```

基底クラスである [[\yii\db\Migration]] が、データベース接続を `db` プロパティによって提供しています。
これを使って、データベースのデータとスキーマを操作することが出来ます。

この例で使われているカラムのタイプは抽象的なタイプであり、Yii によって、あなたが使用するデータベース管理システムに応じて、対応するタイプに置き換えられます。
抽象的なタイプを使うと、データベースに依存しないマイグレーションを書くことが出来ます。
例えば、`pk` は、MySQL では `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY` に置き換えられ、sqlite では `integer PRIMARY KEY AUTOINCREMENT NOT NULL` に置き換えられます。
さらに詳細な説明と、利用可能なタイプについては、[[yii\db\QueryBuilder::getColumnType()]] のドキュメントを参照してください。
また、カラムのタイプを定義するのに、[[yii\db\Schema]] で定義されている定数を使うことも出来ます。

> Note|注意: テーブル定義の最後に、単純な文字列として指定された、制約やその他の特別なテーブルオプションを追加することが出来ます。
> 例えば、上記のマイグレーションでは、`content` 属性の定義の後に、`'CONSTRAINT ...'` やその他の特別なオプションを書くことが出来ます。


トランザクションを使うマイグレーション
--------------------------------------

複雑な DB マイグレーションを実行するときは、通常、データベースの一貫性と整合性を保つために、個々のマイグレーションが全体として成功または失敗することを保証する必要があります。
この目的を達成するために、DB トランザクションを利用することが出来ます。
この目的のためには、`safeUp` と `safeDown` という特別なメソッドを使います。

```php

use yii\db\Schema;

class m101129_185401_create_news_table extends \yii\db\Migration
{
    public function safeUp()
    {
        $this->createTable('news', [
            'id' => 'pk',
            'title' => Schema::TYPE_STRING . ' NOT NULL',
            'content' => Schema::TYPE_TEXT,
        ]);

        $this->createTable('user', [
            'id' => 'pk',
            'login' => Schema::TYPE_STRING . ' NOT NULL',
            'password' => Schema::TYPE_STRING . ' NOT NULL',
        ]);
    }

    public function safeDown()
    {
        $this->dropTable('news');
        $this->dropTable('user');
    }

}
```

使用するクエリが一つだけでない場合は、`safeUp` と `safeDown` を使うことを推奨します。

> Note|注意: 全ての DBMS がトランザクションをサポートしている訳ではありません。
> また、トランザクションに入れることが出来ない DB クエリもあります。
> その場合には、代りに、`up()` と `down()` を実装しなければなりません。
> また、MySQL の場合は、[暗黙のコミット](http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html) を引き起こす SQL 文があります。


マイグレーションを適用する
--------------------------

利用できる全ての新しいマイグレーションを適用する (すなわち、ローカルのデータベースを最新の状態にする) ためには、次のコマンドを実行します。

```
yii migrate
```

コマンドを実行すると、すべての新しいマイグレーションが一覧表示されます。
マイグレーションを適用することを確認すると、クラス名のタイムスタンプの値の順に、一つずつ、すべての新しいマイグレーションクラスの `up()` メソッドが実行されます。

マイグレーションを適用した後に、マイグレーションツールは `migration` という名前のデータベーステーブルに記録を残します。
これによって、ツールは、どのマイグレーションが適用済みで、どのマイグレーションが適用されていないかを特定することが出来ます。
`migration` テーブルが存在しない場合は、ツールは `db` [アプリケーションコンポーネント](structure-application-components.md)によって指定されたデータベースの中にテーブルを自動的に作成します。

時として、新しいマイグレーションを一個また数個だけ適用したい場合があります。その場合は、次のコマンドを使うことが出来ます。


```
yii migrate/up 3
```

このコマンドは、次の新しいマイグレーションを 3 個適用します。3 という値を変更して、適用されるマイグレーションの数を変更することが出来ます。

また、下記のコマンドを使って、特定のバージョンまでデータベースをマイグレートすることも可能です。

```
yii migrate/to 101129_185401
```

すなわち、マイグレーション名のタイムスタンプ部分を使って、データベースをマイグレートして到達したいバージョンを指定します。
最後に適用されたマイグレーションと指定されたマイグレーションの間に複数のマイグレーションがある場合は、それらのマイグレーションがすべて適用されます。
指定されたマイグレーションが適用済みである場合は、その後に適用されたすべてのマイグレーションが取り消されます (次の節で説明します)。


マイグレーションを取り消す
--------------------------

適用された最後のマイグレーション (一個または複数個) を取り消したい場合は、下記のコマンドを使うことが出来ます。


```
yii migrate/down [step]
```

ここで、オプションの `step` パラメータは何個のマイグレーションを取り消すかを指定するものです。
デフォルト値は 1 で、適用された最後のマイグレーションだけが取り消すされることを意味します。

前に説明したように、全てのマイグレーションが取り消せるとは限りません。
取り消せないマイグレーションを取り消そうとすると例外が投げられて、取り消しのプロセス全体が終了させられます。


マイグレーションを再適用する
----------------------------

マイグレーションの再適用とは、指定されたマイグレーションを最初に取り消してから、再度適用することを意味します。
これは次のコマンドによって実行することが出来ます。

```
yii migrate/redo [step]
```

ここで、オプションの `step` パラメータは何個のマイグレーションを再適用するかを指定するものです。
デフォルト値は 1 で、最後のマイグレーションだけが再適用されることを意味します。


マイグレーション情報を表示する
------------------------------

マイグレーションを適用したり取り消したりする他に、マイグレーションツールはマイグレーションの履歴、および、まだ適用されていない新しいマイグレーションを表示することも出来ます。


```
yii migrate/history [limit]
yii migrate/new [limit]
```

ここで、オプションの `limit` パラメータは、何個のマイグレーションを表示するかを指定するものです。
`limit` が指定されない場合は、利用可能な全てのマイグレーションが表示されます。

最初のコマンドは適用済みのマイグレーションを表示し、第二のコマンドはまだ適用されていないマイグレーションを表示します。


マイグレーション履歴を修正する
------------------------------

時として、実際には関係のあるマイグレーションを適用または取り消すことなく、マイグレーション履歴を特定のマイグレーションバージョンに修正したい場合があります。
このことは新しいマイグレーションを開発するときにしばしば起ります。
次のコマンドを使ってこの目的を達することが出来ます。

```
yii migrate/mark 101129_185401
```

このコマンドは `yii migrate/to` コマンドと非常によく似ていますが、マイグレーションを適用または取り消すことなく、マイグレーション履歴テーブルを指定されたバージョンに修正することだけを行うという点で違っています。


マイグレーションコマンドをカスタマイズする
------------------------------------------

マイグレーションコマンドをカスタマイズする方法がいくつかあります。

### コマンドラインオプションを使う

マイグレーションコマンドには、コマンドラインで指定できるいくつかのオプションがあります。

* `interactive`: 真偽値。マイグレーションを対話モードで実行するかどうかを指定します。
  デフォルト値は true で、指定されたマイグレーションを実行するときに、ユーザは何らかの入力を促されます。
  このオプションを false にセットして、マイグレーションがバックグラウンドプロセスとして実行されるようにすることが出来ます。

* `migrationPath`: 文字列。全てのマイグレーションクラスファイルを保存しているディレクトリを指定します。
  このパスは、パスエイリアスの形式で指定されなければならず、また、対応するディレクトリが存在する必要があります。
  このオプションが指定されない場合は、アプリケーションのベースパスの下の `migrations` サブディレクトリが使われます。

* `migrationTable`: 文字列。マイグレーション履歴の情報を保存するためのデータベーステーブル名を指定します。
  デフォルト値は `migration` です。このテーブルは、`version varchar(255) primary key, apply_time integer` という構造を持ちます。

* `db`: 文字列。データベース [アプリケーションコンポーネント](structure-application-components.md) の ID を指定します。
  デフォルト値は 'db' です。

* `templateFile`: 文字列。マイグレーションクラスを生成するためのコードテンプレートとして使われるファイルのパスを指定します。
  このパスは、パスエイリアスの形式で指定しなければなりません (例えば、`application.migrations.template`)。
  指定されない場合は、内部テンプレートが使用されます。
  テンプレートの中の `{ClassName}` というトークンが実際のマイグレーションクラス名によって置き換えられます。

これらのオプションを指定するためには、下記の書式を使って migrate コマンドを実行します。

```
yii migrate/up --option1=value1 --option2=value2 ...
```

例えば、`forum` モジュールのためのマイグレーションを実行するときに、マイグレーションファイルがモジュールの `migrations` ディレクトリに置かれている場合には、下記のコマンドを使うことが出来ます。


```
yii migrate/up --migrationPath=@app/modules/forum/migrations
```


### コマンドをグローバルに構成する

コマンドラインオプションを使ってマイグレーションコマンドをその場その場で構成することも出来ますが、場合によっては、一度にまとめてコマンドを構成しておきたいこともあります。
例えば、マイグレーションの履歴を保存するのに別のテーブルを使用したいとか、カスタマイズしたマイグレーションテンプレートを使用したいとかです。
そうするためには、コンソールアプリケーションの構成情報ファイルを以下のように修正します。


```php
'controllerMap' => [
    'migrate' => [
        'class' => 'yii\console\controllers\MigrateController',
        'migrationTable' => 'my_custom_migrate_table',
    ],
]
```

これで、`migrate` コマンドを実行すると、上記の構成情報が効果を発揮して、毎回コマンドラインオプションを入力する必要がなくなります。
その他のコマンドラインオプションも、このようにして構成することが出来ます。


### 複数のデータベースのマイグレーション

デフォルトでは、マイグレーションは `db` [アプリケーションコンポーネント](structure-application-components.md) によって指定されるデータベースに対して適用されます。
これは、`--db` オプションを指定することによって変更することが出来ます。例えば、

```
yii migrate --db=db2
```

上記のコマンドは、デフォルトのマイグレーションパスに置かれている *全ての* マイグレーションを `db2` データベースに適用するものです。

アプリケーションが複数のデータベースを扱っている場合は、いくつかのマイグレーションはあるデータベースに適用されなければならず、他のマイグレーションは別のデータベースに適用されなければならない、ということがあり得ます。
そのような場合には、異なるデータベースごとに基底マイグレーションクラスを作成して、下記のように [[yii\db\Migration::init()]] メソッドをオーバーライドすることを推奨します。

```php
public function init()
{
    $this->db = 'db2';
    parent::init();
}
```

こうすると、特定のデータベースに適用されるべきマイグレーションを作成するためには、対応する基底マイグレーションクラスから拡張するだけで済みます。
これで、`yii migrate` コマンドを実行すると、全てのマイグレーションはそれぞれ対応するデータベースに対して適用されるようになります。

> Info|情報: それぞれのマイグレーションはハードコードされた DB 接続を使用しますので、`migrate` コマンドの `--db` オプションは効果を持ちません。
  また、マイグレーション履歴はデフォルトの `db` データベースに保存されることに注意してください。

`--db` オプションによる DB 接続の変更をサポートしたい場合は、次のような別の方法で複数のデータベースを扱うことが出来ます。

それぞれのデータベースについて、マイグレーションパスを作成し、対応する全てのマイグレーションクラスをそこに保存します。
マイグレーションを適用するためには、次のようなコマンドを実行します。

```
yii migrate --migrationPath=@app/migrations/db1 --db=db1
yii migrate --migrationPath=@app/migrations/db2 --db=db2
...
```

> Info|情報: 上記の方法では、マイグレーション履歴は `--db` オプションによって指定された別々のデータベースに保存されます。
