PHP
Phinx

Phinx導入ガイド

More than 1 year has passed since last update.

導入のための留意点

筆者は、CakePHP3での採用と他のフレームワークでも非依存で利用できるからという理由で導入をした。

しかしCakeDCのmigrations pluginと同じ考え方なんだろうという先入観があったために、かえって理解しづらい点があったので、そういう方は次の点を念頭に置くと悩みが少ない(かもしれない)。

  • 既にあるDBをリバースエンジニアリングする機能はない。
    • CakePHPのschema.phpに相当する概念がない。
  • DDLをSQLではなく、phpのコードで書けるのが利点…と捉えた方がいい。
    • マイグレーションをPhinxの管理下に置くということは、何もない状況からマイグレーションできるようにすべきだから、既存のDDLがある場合は書き換える必要がある。
    • SQLを書けるメソッド(executeやquery)があるので、ヒアドキュメントを使う方法はある。
  • 既にDBを構成している環境では使いにくい。
    • Phinxでは専用の履歴テーブル(デフォルトでPhinxlog)を作って、マイグレーションがどこまで適用しているかのステータスを保持する。
    • 既にDB展開済の場合は、Phinxが持つステータス状況と実態が合わずに、問題が起こりうる。
    • それを回避するには、マイグレーションのup/downメソッドにテーブルやフィールド(コラム)の実在確認の処理を書くか、初回を空っぽにして、2回目からまじめに書く。(ただしDDLは別提供)
    • または、初回マイグレーションだけぶっ放し、2回目からまじめに書く。

導入

既にリポジトリに入っており、利用者としてセットアップしたい人はインストールの項から参照のこと。

展開ディレクトリの作成

Composerを使うので、他のライブラリーをComposerでインストールしている場合は、同じvendorsに展開される。

もし独立した環境にしたい場合は、別にディレクトリを作り、その中で展開する。

gitignoreの設置

お好みでgitignoreを設置する。
composerでインストールできるものはリポジトリ管理に入れず、自分たちが書いたものだけを入れるというセオリーがあるので、全部をコミットするのは推奨しない。

$ vi .gitignore
vendor
Phinx.yml

composer

composer.jsonを配置

$ vi composer.json
{ 
    "require":{ 
        "robmorgan/Phinx": "*" 
    } 
} 

もし、チームで、環境にcomposerをインストールしない場合は、composer.pharをプロジェクトに配置してコミットしておく。

$ curl -s http://getcomposer.org/installer | php 

インストール

環境にcomposerをインストール済の場合は

$ composer install 

composerをインストールされていない場合は、composer.pharを使う。

$ php composer.phar install 

Phinxが使用できる状態になったので、環境定義(Phinx.yml)を生成し、DB情報を編集する。

$ ./vendor/bin/Phinx init
$ vi Phinx.yml

environmentsのところに、production, development, testingの3つの環境定義があるが、デフォルトはdevelopmentなので、複数の環境を使い分けない場合は、developmentのところだけ書き換える。

    development:
        adapter: mysql
        host: localhost
        name: (データベース名)
        user: (ログインID)
        pass: (パスワード)
        port: 3306
        charset: utf8

マイグレーション処理の流れ

詳細はそれぞれ後述ので、概要として流れを解説する。

  1. マイグレーション定義ファイルを生成する
    ファイルを作って、定義が書ける準備をする。

  2. マイグレーション定義ファイルに処理を書く。
    定義ファイルの中にchange, up, downのメソッドが生成されるので、これに記述を行う。

  3. マイグレーションを実行する。
    これによりDBに変更が実行され、Phinxlogテーブルが作成され、どこまでマイグレーションされているかを記録する。

以降、変更を加えるごとに1〜3を実行する。gitにおけるプル→コミットのように、3→1→2→3となる。
マイグレーションを反映させるには、3だけを実行する。

留意すべきは、リバースしないのでDBを直接編集せず、定義ファイルに記述し、それを実行することでDBに変更を与えること。

1.マイグレーション定義ファイルの生成

まず空の定義ファイルを作り、そこに処理を書いてマイグレーション定義が完成する。

次のコマンドで、定義ファイルが作成される。定義ファイルを格納するフォルダ(デフォルトでは ./migrations)が存在していない場合は、自動的に作成される。

$ ./vendor/bin/Phinx create (定義名)

定義名とは、マイグレーション定義ファイルに付ける名前であり、その中で定義されるクラス名になる。

公式ドキュメントでは「MyNewMigration」という名前になっているが、何をしたのか分かる名前を付ける方がよい。(statusを参照

定義名は、CamelCaseでなければならない。
「initial」はエラーになり、「Initial」はOK。

これで、./migrations/20150614151105_initial.php という日付+定義名でファイルが作成される。

ちなみに、この時点でPhinx.ymlを設定していなくてもエラーにならないことからも、マイグレーションの実行以外ではDBへのアクセスはない。

2.マイグレーションファイルに処理を書く

作成された定義ファイルを開く。

$ vi ./migrations/20150614151105_initial.php (例)

内容としては、change, up, downの3つのメソッドがあり、changeは最初コメントアウトされている(後述の通り、up/downと排他的に動作するため)。

メソッド 処理
up マイグレーションするときに実行される
down ロールバックするときに実行される
change マイグレーションするときに実行されるが、ロールバックする場合も、逆処理を行う。なお、このメソッドが有効(宣言されている状態)の場合、up/downは実行されない。

逆処理とは、addColumnの場合はremoveColumnが実行されることを意味する。当然全てが逆処理できるわけではなく、対応しているのは下記の通り。

changeメソッド内のコマンド(migrateで実行) rollbackで実行されるコマンド
createTable dropTable
renameTable renameTable(引数逆)
addColumn removeColumn
renameColumn renameColumn(引数逆)
addIndex removeIndex
addForeignKey dropForeignKey

これ以外の処理を行う場合は、up/downでそれぞれ処理を書く方がよい。

テーブル操作

$table = $this->table(テーブル名);

ここで代入した$tableに対して、処理を行う。

テーブルの追加

addColumnとコラム追加をしてから、create()メソッドを呼ぶ。
addColumnについては後述

$table->addColumn('name', 'string', array('limit' => 100)
      ->addColumn('age', 'integer')
      ->create();

create/updateとsave

createはテーブル生成を明示するものだが、公式ドキュメントではcreateではなく、saveメソッドを呼ぶと書かれている。
saveは、createまたはupdateになるので、基本的にup/downメソッドに記述する場合はsaveでよい。

しかしchangeメソッドで定義する場合は、公式ドキュメントにも書かれている通り、createまたはupdateを明示しなければならないと書かれている。理由はrollbackするときにコラムを消すのか、droptableするのかが自動的には判断できないため。
そのため、このドキュメントではcreate/updateを使用している。

テーブルの削除

$this->dropTable('users');

列操作

列の追加

$table->addColumn('列名', '型' , array(オプション))
      ->addColumn(…繰り返し…)

列名

フィールド名

型には次のものが使用できる。…と言いたいところだが、(お察し下さい)なので、公式ドキュメント参照。

なおstringは、limitオプションを付けない限り、デフォルトではvarchar(255)。

オプション

(お察し下さい)なので、公式ドキュメント参照。

列の削除

$table->removeColumn('列名')
      ->update()

列名の変更

$table->renameColumn('変更前', '変更後');

インデックス

addColumnなどと組み合わせて定義する。

$table->addIndex(array('列名1', '列名n'))
      ->update()

既にテーブルが存在しているかどうか

$exists = $this->hasTable('users');
if ($exists) {
     
}

SQLの実行

executeとqueryがあるが、返値に違いがある。

//影響を及ぼしたレコード数が返値
$count = $this->execute('DELETE FROM users');
//実行した結果が返値
$rows = $this->query('SELECT * FROM users');

既に作成済みのDDLをぶっ放す。

    public function up()
    {
        $query = <<<EOQ
CREATE TABLE IF NOT EXISTS `users` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(255) default NULL,
  `age` integer,
  `created` datetime default NULL,
  `modified` datetime default NULL,
  PRIMARY KEY  (`id`)
)AUTO_INCREMENT=1 ;
EOQ;
        $this->execute($query);
    }

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

idの自動生成を抑制する

Phinxは、id列を自動的に追加する。自分でid列の仕様を指定したり、primary keyを変更したい場合は邪魔になる。その場合は、下記のようにテーブル指定時にオプションで指定する。

$table = $this->table('statuses',
    [
        'id' => false,
        'primary_key' => ['id']
    ]);
$table->addColumn('id', 'char', ['limit' => 36])
      ->addColumn('name', 'char', ['limit' => 255])
      ->addColumn('model', 'string', ['limit' => 128])
      ->create();

出典

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

migrate

マイグレーションの実行。マイグレーション定義ファイルのupメソッドまたはchangeメソッドを実行する。(changeメソッドがある場合は、upメソッドは無視される)
後述の-tオプションなしでは、最新まで一括でマイグレーションする。

$ ./vendor/bin/Phinx migrate

rollback

1つずつ戻す。downメソッドまたはchangeメソッドの逆処理を実行する。(up同様changeメソッドがある場合はdownメソッドは無視される。)

$ ./vendor/bin/Phinx rollback 

ターゲットを指定する

ターゲットを指定したい場合は、-tオプションを付け、マイグレーションIDを指定する。

例)20150614154730_my_migration.phpの場合

$ ./vendor/bin/Phinx migrate -t 20150614154730

status

全てのマイグレーション一覧を表示し、かつ現在のマイグレーション適用状況を表示する。

$ ./vendor/bin/Phinx status
 Status  Migration ID    Migration Name
-----------------------------------------
     up  20150614070924  InitialTable
     up  20150615021023  Users
   down  20150615111810  Logs
   down  20150615112743  Contents

この場合、Usersまでは適用されているが、Logsから適用されていないことを示している。