You are currently reading the 1.2 documentation. Switch to 2.2  2.1  2.0 

Migrations

Doctrineのマイグレーションパッケージのプログラミングインターフェイスを通して本番のデータベースを簡単に更新できます。データベースがバージョン管理されバージョンを通して差し戻しできるように変更が行われます。

マイグレーションを実行する

マイグレーションクラスの作り方を学んだので次のセクションでDoctrinenのテスト環境でマイグレーションを実装できるようにマイグレーションの実行の仕方を見てみましょう。

最初にDoctrine_Migrationの新しいインスタンスを作りこれにマイグレーションクラスへのパスを渡しましょう:

$migration = new Doctrine_Migration('/path/to/migration_classes', $conn);

migrate()メソッドを呼び出すことで最新バージョンに移行できます:

$migration->migrate();

特定のバージョンにマイグレートするにはmigrate()に引数を渡します。例えばバージョン0から3にマイグレートできます:

$migration->migrate(3);

バージョン3から0に戻すことができます:

$migration->migrate(0);

データベースの現在のバージョンを取得するにはgetCurrentVersion()メソッドを使います:

echo $migration->getCurrentVersion();

migrate()メソッドのバージョン番号の引数を省略するとDoctrineは内部でディレクトリで見つかるクラスの最新バージョン番号にマイグレートしようとします。

マイグレーションのトランザクション
内部ではDoctrineはトランザクションのマイグレーションバージョンをラップしません。マイグレーションクラスで例外とトランザクションを処理するのは開発者しだいです。トランザクションDDLをサポートするデータベースはごくわずかであることを覚えておいてください。大抵のデータベースでは、トランザクションでマイグレーションをラップする場合でも、create、alter、drop、renameなどのステートメントは効果があります。

実装

マイグレーションの実施方法を理解したのでmigrate.phpという名前でテスト環境のスクリプトを実装してみましょう。

最初にマイグレーションクラスを保存する場所が必要なのでmigrationsという名前のディレクトリを作りましょう:

$ mkdir migrations

migrate.phpスクリプトを作り次のコードを記入します:

// migrate.php

require_once('bootstrap.php');

$migration = new Doctrine_Migration('migrations');
$migration->migrate();

マイグレーションクラスを書く

マイグレーションクラスはDoctrine_Migrationを継承するシンプルなクラスで構成されます。up()down()メソッドを定義します。これらのメソッドはそれぞれ指定されたマイグレーションバージョンでのデータベースの変更とその取り消しを意味します。

クラスの名前がなんであれ、正しい順序でマイグレーションをロードするために使われる数字が含まれる接頭辞をクラスが含まれるファイルの名前につけなければなりません。

バージョン1から始まるデータベースをビルドするために使うマイグレーションクラスの例です。

最初のバージョンとしてmigration_testという名前の新しいテーブルを作りましょう:

// migrations/1_add_table.php

class AddTable extends Doctrine_Migration_Base
{
    public function up()
    {
        $this->createTable('migration_test', array('field1' => array('type' => 'string')));
    }

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

前のバージョンで追加したテーブルに新しいカラムを追加した2番目のバージョンを作りましょう:

// migrations/2_add_column.php

class AddColumn extends Doctrine_Migration_Base
{
    public function up()
    {
        $this->addColumn('migration_test', 'field2', 'string');
    }

    public function down()
    {
        $this->removeColumn('migration_test', 'field2');
    }
}

最後に、field1カラムの型を変更してみましょう:

// migrations/3_change_column.php

class ChangeColumn extends Doctrine_Migration_Base
{
    public function up()
    {
        $this->changeColumn('migration_test', 'field2', 'integer');
    }

    public function down()
    {
        $this->changeColumn('migration_test', 'field2', 'string');
    }
}

3つのマイグレーションクラスを作成したので以前実装したmigrate.phpスクリプトを実行できます:

$ php migrate.php

データベースを見るとmigrate_testという名前のテーブルが存在しmigration_versionのバージョン番号が3に設定されることが確認できます。

最初の状態に差し戻したい場合migrate.phpスクリプトでmigrate()メソッドにバージョン番号を渡します:

// migrate.php

// ...
$migration = new Doctrine_Migration('migrations');
$migration->migrate(0);

そしてmigrate.phpスクリプトを実行します:

$ php migrate.php

データベースを見ると、up()メソッドで行ったすべての内容がdown()メソッドの内容によって差し戻されます。

利用可能なオペレーション

マイグレーションクラスでデータベースを変えるために利用できるメソッドの一覧は次の通りです。

テーブルを作成する

// ...
    public function up()
    {
        $columns = array(
            'id' => array(
                'type' => 'integer',
                'unsigned' => 1,
                'notnull' => 1,
                'default' => 0
            ),
            'name' => array(
                'type' => 'string',
                'length' => 12
            ),
            'password' => array(
                'type' => 'string',
                'length' => 12
            )
        );

        $options = array(
            'type'     => 'INNODB',
            'charset'  => 'utf8'
        );

        $this->createTable('table_name', $columns, $options);
    }
// ...

スキーマを操作するために使われるデータ構造とデータベース抽象化レイヤーで使われるデータ構造が同じであることにお気づきかもしれません。これはマイグレーションクラスで指定されているオペレーションを実行するために内部でマイグレーションパッケージがデータベース抽象化レイヤーを使用しているからです。

テーブルを削除する

// ...
    public function down()
    {
        $this->dropTable('table_name');
    }
// ...

テーブルをリネームする

// ...
    public function up()
    {
        $this->renameTable('old_table_name', 'new_table_name');
    }
// ...

制約を作成する

// ...
    public function up()
    {
        $definition = array(
            'fields' => array(
                'username' => array()
            ),
            'unique' => true
        );

        $this->createConstraint('table_name', 'constraint_name', $definition);
    }
// ...

制約を削除する

Now the opposite down() would look like the following:

// ...
    public function down()
    {
        $this->dropConstraint('table_name', 'constraint_name');
    }
// ...

外部キーを削除する

// ...
    public function up()
    {
        $definition = array(
            'local'         => 'email_id',
            'foreign'       => 'id',
            'foreignTable'  => 'email',
            'onDelete'      => 'CASCADE'
        );

        $this->createForeignKey('table_name', 'email_foreign_key', $definition);
    }
// ...

$definition用の有効なオプションは次の通りです:

名前 説明
name オプションの制約名
local ローカルフィールド
foreign 外部参照フィールド
foreignTable 外部テーブルの名前
onDelete 参照の削除アクション
onUpdate 参照の更新アクション
deferred 延期された制約チェック

外部キーを削除する

// ...
    public function down()
    {
        $this->dropForeignKey('table_name', 'email_foreign_key');
    }
// ...

カラムを追加する

// ...
    public function up()
    {
        $this->addColumn('table_name', 'column_name', 'string', $length, $options);
    }
// ...

カラムをリネームする

sqliteのような一部のDBMSはカラムのリネームオペレーションを実装していません。sqlite接続を使用している場合カラムをリネームしようとすると例外が投げられます。

// ...
    public function up()
    {
        $this->renameColumn('table_name', 'old_column_name', 'new_column_name');
    }
// ...

カラムを変更する

既存のカラムのアスペクトを変更する:

// ...
    public function up()
    {
        $options = array('length' => 1);
        $this->changeColumn('table_name', 'column_name', 'tinyint', $options);
    }
// ...

カラムを削除する

// ...
    public function up()
    {
        $this->removeColumn('table_name', 'column_name');
    }
// ...

不可逆なマイグレーション

リバースできないup()メソッドでオペレーションを実行することがあります。例えばテーブルからカラムを削除する場合です。この場合新しいDoctrine_Migration_IrreversibleMigrationException例外を投げる必要があります。

// ...
    public function down()
    {
        throw new Doctrine_Migration_IrreversibleMigrationException(
            'The remove column operation cannot be undone!'
        );
    }
// ...

インデックスを追加する

// ...
    public function up()
    {
        $options = array('fields' => array(
                            'username' => array(
                                'sorting' => 'ascending'
                            ),
                            'last_login' => array()));

        $this->addIndex('table_name', 'index_name', $options)
    }
// ...

インデックスを削除する

// ...
    public function down()
    {
        $this->removeIndex('table_name', 'index_name');
    }
// ...

プレフックとポストフック

モデルでデータベースのデータを変えることが必要な場合があります。テーブルを作成もしくは変更するのでup()もしくはdown()メソッドが処理された後でデータを変更しなければなりません。preUp()postUp()preDown()、とpostDown()という名前でフックを用意します。定義すればこれらのメソッドは実行されます:

// migrations/1_add_table.php

class AddTable extends Doctrine_Migration_Base
{
    public function up()
    {
        $this->createTable('migration_test', array('field1' => array('type' => 'string')));
    }

    public function postUp()
    {
        $migrationTest = new MigrationTest();
        $migrationTest->field1 = 'Initial record created by migrations';
        $migrationTest->save();
    }

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

上記の例はMigrationTestモデルを作成し利用可能にしたことを前提とします。up()メソッドが実行されるとmigration_testテーブルが作成されるのでMigrationTestモデルが使われます。このモデルの定義は下記の通りです。

// models/MigrationTest.php

class MigrationTest extends Doctrine_Record
{
    public function setTableDefinition()
    {
        $this->hasColumn('field1', 'string');
    }
}

YAMLフォーマットでの例は次の通りです。YAML Schema Filesの章でYAMLの詳細を学びます:

---
# schema.yml

# ...
MigrationTest:
  columns:
    field1: string

Up/Downの自動化

Doctrineのマイグレーション機能では大抵の場合マイグレーションメソッドの反対側を自動化することが可能です。例えばマイグレーションのupで新しいカラムを作成する場合、downを簡単に自動化するのは可能で必要なのは作成されたカラムを削除することです。これはupdownの両方に対してmigrate()メソッドを使用して実現可能です。

migrate()メソッドは$directionの引数を受け取りupもしくはdownの値を持つようになります。この値はcolumntable、のようなメソッドの最初の引数に渡されます。

カラムの追加と削除を自動化する例は次の通りです。

class MigrationTest extends Doctrine_Migration_Base
{
    public function migrate($direction)
    {
      $this->column($direction, 'table_name', 'column_name', 'string', '255');
    }
}

上記のマイグレーションでupを実行するときカラムが追加され、downが実行されるときカラムが削除されます。

自動化できるマイグレーションメソッドのリストは次の通りです:

自動メソッド名 自動化
table() createTable()/dropTable()
constraint() createConstraint()/dropConstraint()
foreignKey() createForeignKey()/dropForeignKey()
column() addColumn()/removeColumn()
index() addInex()/removeIndex()

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

Doctrineはいくつかの異なる方法でマイグレーションクラスを生成する機能を提供します。既存のデータベースを再現するマイグレーションのセットを生成する、もしくは既存のモデルのセット用にデータベースを作成するマイグレーションクラスを生成します。2つのスキーマ情報の2つのセットの間の違いからマイグレーションを生成することもできます。

データベースから

既存のデータベース接続からマイグレーションのセットを生成するには、Doctrine_Core::generateMigrationsFromDb()を使います。

Doctrine_Core::generateMigrationsFromDb('/path/to/migration/classes');

既存のモデルから

既存のモデルのセットからマイグレーションのセットを生成するには、Doctrine_Core::generateMigrationsFromModels()を使うだけです。

Doctrine_Core::generateMigrationsFromModels('/path/to/migration/classes', '/path/to/models');

差分ツール

ときにはモデルを変更して変更に対するマイグレーション処理を自動化できるようにしたいことがあります。以前は変更に対してマイグレーションクラスを書かなければなりませんでした。しかし差分ツールによって変更を行い変更用のマイグレーションクラスを生成できます。

差分ツールはシンプルで使いやすいです。これは"from"と"to"を受け取り、これらは次のうちのどれかになります:

  • YAMLスキーマファイルへのパス
  • 既存のデータベース接続の名前
  • モデルの既存のセットへのパス

2つのYAMLスキーマファイルを作るシンプルな例を考えます。1つはschema1.ymlでもう1つはschema2.ymlという名前です。

schema1.ymlはシンプルなUserモデルを含みます:

---
# schema1.yml

User:
  columns:
    username: string(255)
    password: string(255)

スキーマを修正してemail_addressカラムを追加する場合を考えてみましょう:

---
# schema1.yml

User:
  columns:
    username: string(255)
    password: string(255)
    email_address: string(255)

これでデータベースに新しいカラムを追加できるマイグレーションクラスを簡単に作ることができます:

Doctrine_Core::generateMigrationsFromDiff('/path/to/migration/classes', '/path/to/schema1.yml', '/path/to/schema2.yml');

これによって/path/to/migration/classes/1236199329_version1.phpのパスでファイルが生み出されます。

class Version1 extends Doctrine_Migration_Base
{
    public function up()
    {
        $this->addColumn('user', 'email_address', 'string', '255', array ());
    }

    public function down()
    {
        $this->removeColumn('user', 'email_address');
    }
}

データベースを簡単にマイグレートして新しいカラムを追加できます!

まとめ

安全かつ簡単にスキーマを変更できるので本番のデータベーススキーマを変更するためにマイグレーション機能は大いに推奨されます。

マイグレーションはこの本で検討する最後の機能です。最後の章では日常業務で手助けになる他のトピックを検討します。最初に他のUtilitiesを検討しましょう。


Questions and Feedback

If you find a problem with the documentation or have a suggestion, please register and open a ticket.

If you need support or have a technical question, you can post to the user mailing-list.