CakePHP3にも採用されているマイグレーションツール「Phinx」を使ってみた

  • このエントリーをはてなブックマークに追加
  • Evernoteに保存

こんにちは。リスペクトの木村です。

以前に書いた記事、「Vagrant環境で実現するお手軽DBマイグレーション」の中で、「Flyway」というJavaで動作するマイグレーションツールをご紹介しました。
Flywayは言語やアプリケーションを問わず使えたり、生のSQLクエリで記述するので汎用性が高い点が良かったのですが、別途Javaが必要であるとか、up(マイグレーション)はできてもdown(ロールバック)が出来ないという難点がありました。

サイトのリニューアルにあたりマイグレーション周りを整備する必要があったため、難点が解消できるツールを探した所、「Phinx」というマイグレーションツールがありました。
使ってみた所非常に良い感じでしたので、今回はそちらをご紹介します。

今回の環境

  • CentOS 7
  • PHP 5.6.14
  • MySQL 5.7.9
  • Phinx 0.4.6

What’s Phinx?

PHPで書かれたPHP向けのマイグレーションツールです。
PHP 5.3.2以降が動いている環境(Phinx 0.4.6時点)であれば簡単に使い始める事ができます。

Flywayや他のマイグレーションツールと比較してみた特徴はこんな感じです。

  • インストールや設定が簡単
  • フレームワーク依存ではない
  • 軽い
  • ORM/SQL文の両方の記述に対応
    • 後述しますが、ORMを使った方がより多くの恩恵を受けられます
  • MySQL/PostgreSQL/SQLite/SQL Serverに対応

ちなみに、CakePHP3のマイグレーションにも採用されています。
(という事は、CakePHP2系のmigration pluginとは互換性が無いという事になります。トホホ。)

導入方法

お手元のcomposer.jsonに、次のようにrequire内に追加してinstallを実行するだけです。
(*で設定していますが、バージョンなどは環境に合わせて下さい。)

{
    "require":{
        "robmorgan/phinx": "*"
    }
}

composerコマンド(もしくはcomposer.phar)からでも追加できます。

$ composer require robmorgan/phinx

インストールすると、vendor/bin/phinx(vendor/bin/phinx.bat) に実行スクリプトが、vendor/robmorgan/phinx 以下にライブラリがインストールされます。

設定

まずはvendor/bin/phinx initで初期化を行い、phinx.ymlという設定ファイルをを作成します。
phinx.ymlにはデータベース接続設定などが保存されます。デフォルトではYAMLで生成されますが、phinx.jsonやphinx.phpというファイルを用意する事でJSONやPHPを生成することもできます。

生成されるファイルの中身はこんな感じです。

paths:
    migrations: %%PHINX_CONFIG_DIR%%/migrations

environments:
    default_migration_table: phinxlog
    default_database: development
    production:
        adapter: mysql
        host: localhost
        name: production_db
        user: root
        pass: ''
        port: 3306
        charset: utf8

    development:
        adapter: mysql
        host: localhost
        name: development_db
        user: root
        pass: ''
        port: 3306
        charset: utf8

    testing:
        adapter: mysql
        host: localhost
        name: testing_db
        user: root
        pass: ''
        port: 3306
        charset: utf8

必要に応じ、このファイルの設定内容を手動で変更していきます。

paths:migrations:はマイグレーションファイルの保存先を指定します。
デフォルトはphinx.ymlのあるディレクトリ(%%PHINX_CONFIG_DIR%%)のmigrationsディレクトリを指しています。
マイグレーションファイルの作成時にディレクトリが存在しない場合は作成してくれます。

environments:default_migration_tableはマイグレーション情報を管理するテーブル名を設定できます。基本的にはデフォルトで良いのですが、命名ルールにポリシーがある場合など必要に応じて変更しておきます。
ちなみに、このテーブルは初回マイグレーション時に勝手に作成されます。

お次のenvironments:default_databaseは環境を指定しない場合のデフォルトで使用する環境設定名です。
引数でその設定を使用するか指定できますので、変更する必要はありません。

environments:productionenvironments:developmentenvironments:testingは環境毎の接続設定を記述します。
この3つしか使えない訳では無いので、設定を追加したり減らしたりする事も可能です。

他の設定についてはドキュメントを確認してください。
ソケット接続や接続先DBMSに合わせた設定もそこそこ細かく行えるようです。

マイグレーションの作成

テーブル作成のマイグレーション

準備が整ったので、実際にマイグレーションファイルを作成します。
作成はvendor/bin/phinx create マイグレーション名で、マイグレーション名はキャメルケースで指定します。
今回はmembersテーブルを作成する、という事にするので、AddMembersTableという名前にしました。

$ vendor/bin/phinx create AddMembersTable

ちなみに、マイグレーション名がキャメルケースではなかった場合は、下記のようにExceptionとエラーメッセージが表示されます。

[vagrant@localhost html]$ vendor/bin/phinx create addMembersTable
Phinx by Rob Morgan - https://phinx.org. version 0.4.6

using config file ./phinx.yml
using config parser yaml
using migration path /var/www/html/migrations



  [InvalidArgumentException]
  The migration class name "addMembersTable" is invalid. Please use CamelCase format.

コマンドを実行するとmigrationsディレクトリの中に日付と時間_マイグレーション名.phpという命名規則で、空のマイグレーションファイルが作成されます。

中身は以下のような感じです。

<?php

use PhinxMigrationAbstractMigration;

class AddMembersTable extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {

    }
}

作成直後はchange()メソッドしかありませんので、基本的にはこの中に記述します。

今回はmembersテーブルを作成するので、こんなようになります。

    public function change()
    {
        $table = $this->table('members');
        $table->addColumn('name', 'string')
            ->addColumn('age', 'integer')
            ->addColumn('created', 'datetime')
            ->addColumn('modified', 'datetime')
            ->addColumn('deleted', 'boolean')
            ->create();
    }

$this->table()でテーブル名を指定してインスタンスを取得し、それに対してaddColumnやaddIndexをして最後にcreate()でテーブルを作成します。
addColumnだけでは作成してくれないので注意が必要です。

addColumnの関数定義はこのようになっています。

public function addColumn($columnName, $type = null, $options = array())

$columnNameがカラム名、$typeはカラムの種類、$optionsはオプション(サイズやデフォルト値、NULLの許可不許可など)です。
カラムのタイプについては後ほど説明します。また、オプションについては色々あるため、ドキュメントを参照下さい。

また、主キーのカラムは「id」という名前で勝手に作成してくれます。(int(11) AUTO INCREMENT)
そのため、addColumnでid名のカラムを作成しようとするとエラーになるので注意が必要です。
idが不要な場合は、$this->table()の第二引数に下記のようなオプションを渡します。

$this->table('members', array('id' => false));

また、主キーを「id」以外の名前にする場合は、上記のfalseを渡している部分を、設定したい名前を渡すようにします。
「id」と同じく指定した名前でカラムを作成してくれますので、addColumnは不要です。

$this->table('members', array('id' => ‘member_id’));

changeメソッド内でのテーブル操作は次の関数のみ利用可能です。
(他の関数を利用すると、IrreversibleMigrationExceptionの例外がdown時に発生します。)
これらを利用して記述する事で、up時やdown時にマイグレーションの内容を自動的に調整して実行します。

  • createTable
  • renameTable
  • addColumn
  • renameColumn
  • addIndex
  • addForeignKey

例えば、addColumnを使用した場合は、up時にカラムを追加し、down時はカラムを削除する、という動きをしてくれます。
カラム操作系ですとaddColumnやrenameColumnの他に、removeColumnというのもあるのですが、これを利用する場合はchangeメソッドが利用できないため、upやdownへ必要な処理を記述する事になります。
insertでデータを追加する時や、queryを用いて直接SQLクエリを実行する場合も同様に、upやdownへ必要な処理を記述する必要があります。

カラム追加のマイグレーション

お次はカラムを追加してみます。

UUID用のカラムを追加する、という事で進めます。マイグレーションファイルの名前は、ベタベタにAddUuidColumnAtMembersTableに。

$ vendor/bin/phinx create AddUuidColumnAtMembersTable

中身はこうなります。テーブル作成時とあまり変わらない感じです。

   public function change()
   {
       $table = $this->table('members');
       $table->addColumn('uuid', 'uuid')
           ->save();
   }

最後のcreate()がsave()になってますが、save()は保存用のメソッドです。テーブルの有無をチェックして、無い場合はテーブルを作成してくれます。
となると、先ほどのテーブル作成時もcreate()ではなくsave()で良いのですが、ここでは分かりやすくするため使い分ける事にします。

カラムの種類

カラムは下記のようなタイプに対応しています。

  • biginteger
  • binary
  • boolean
  • date
  • datetime
  • decimal
  • float
  • integer
  • string
  • text
  • time
  • timestamp
  • uuid

MySQLの場合、上記に加えてenumsetにも対応しています。
特にオプションを指定しない場合の、MySQLとの対応表は下記のような感じです。

Phinx MySQL
biginteger bigint(20) NOT NULL
binary blob NOT NULL
boolean tinyint(1) NOT NULL
date date NOT NULL
datetime datetime NOT NULL
decimal decimal(10,0) NOT NULL
float float NOT NULL
integer int(11) NOT NULL
string var_char(255) NOT NULL
text text NOT NULL
time time NOT NULL
timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
uuid char(36) NOT NULL
enum enum(オプションの「values」で指定した内容) NOT NULL
set set(オプションの「values」で指定した内容) NOT NULL

migrate / rollback

記述もできた所で、マイグレーションを実行します。
phinx migrateで実行しますが、そのままですとenvironments:default_databaseの環境で実行されるため、-eオプションで環境を指定します。
development環境に対して実行する場合は下記のような感じです。

$ vendor/bin/phinx migrate -e development

実行するとこんなログが流れます。

[vagrant@localhost html]$ vendor/bin/phinx migrate -e development
Phinx by Rob Morgan - https://phinx.org. version 0.4.6

using config file ./phinx.yml
using config parser yaml
using migration path /var/www/html/migrations
using environment development
using adapter mysql
using database development_db

 == 20151023110916 AddMembersTable: migrating
 == 20151023110916 AddMembersTable: migrated 0.0079s

 == 20151023122445 AddUuidColumnAtMembersTable: migrating
 == 20151023122445 AddUuidColumnAtMembersTable: migrated 0.0077s

All Done. Took 0.0198s

確認すると、確かにテーブルが作成されています。

mysql> show tables;
+--------------------------+
| Tables_in_development_db |
+--------------------------+
| members                  |
| phinxlog                 |
+--------------------------+
2 rows in set (0.00 sec)

mysql> show columns from members;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| name     | varchar(255) | NO   |     | NULL    |                |
| age      | int(11)      | NO   |     | NULL    |                |
| created  | datetime     | NO   |     | NULL    |                |
| modified | datetime     | NO   |     | NULL    |                |
| deleted  | tinyint(1)   | NO   |     | NULL    |                |
| uuid     | char(36)     | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

ちなみに、phinxlogはマイグレーション情報管理のテーブルです。

ロールバックは下記のように実行します。

$ vendor/bin/phinx rollback -e development

実行時のログはこんな感じで、AddUuidColumnAtMembersTableが戻ります。

[vagrant@localhost html]$ vendor/bin/phinx rollback -e development
Phinx by Rob Morgan - https://phinx.org. version 0.4.6

using config file ./phinx.yml
using config parser yaml
using migration path /var/www/html/migrations
using environment development
using adapter mysql
using database development_db

 == 20151023122445 AddUuidColumnAtMembersTable: reverting
 == 20151023122445 AddUuidColumnAtMembersTable: reverted 0.0114s

All Done. Took 0.0152s

実行後に確認すると、UUIDカラムが無くなりました。

mysql> show columns from members;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| name     | varchar(255) | NO   |     | NULL    |                |
| age      | int(11)      | NO   |     | NULL    |                |
| created  | datetime     | NO   |     | NULL    |                |
| modified | datetime     | NO   |     | NULL    |                |
| deleted  | tinyint(1)   | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

もう一回実行すると・・・

[vagrant@localhost html]$ vendor/bin/phinx rollback -e development
Phinx by Rob Morgan - https://phinx.org. version 0.4.6

using config file ./phinx.yml
using config parser yaml
using migration path /var/www/html/migrations
using environment development
using adapter mysql
using database development_db

 == 20151023110916 AddMembersTable: reverting
 == 20151023110916 AddMembersTable: reverted 0.0067s

All Done. Took 0.0104s

テーブルが無くなりました。

mysql> show tables;
+--------------------------+
| Tables_in_development_db |
+--------------------------+
| phinxlog                 |
+--------------------------+
1 row in set (0.00 sec)

migrate/rollback共に-tオプションで対象のバージョン(マイグレーションファイル名先頭の日付の部分)を指定できます。
また、rollbackのみ-t 0で全てのバージョンをロールバックして初期の状態に戻します。

status

statusコマンドを使用する事で、現在のマイグレーションの状態を確認する事ができます。

[vagrant@localhost html]$ vendor/bin/phinx status -e development
Phinx by Rob Morgan - https://phinx.org. version 0.4.6

using config file ./phinx.yml
using config parser yaml
using migration path /var/www/html/migrations
using environment development

 Status  Migration ID    Migration Name
-----------------------------------------
   down  20151023110916  AddMembersTable
   down  20151023122445  AddUuidColumnAtMembersTable

全てロールバックした後ですので、「Status」が全てdownになっています。

再度マイグレーションすると、全てupになっています。

[vagrant@localhost html]$ vendor/bin/phinx status -e development
Phinx by Rob Morgan - https://phinx.org. version 0.4.6

using config file ./phinx.yml
using config parser yaml
using migration path /var/www/html/migrations
using environment development

 Status  Migration ID    Migration Name
-----------------------------------------
     up  20151023110916  AddMembersTable
     up  20151023122445  AddUuidColumnAtMembersTable

まとめ

ここまでPhinxのご紹介でした。
コマンド数も少なく覚えやすいですし、自動判定のおかげでマイグレーションを書く時間が少なくなりましたので良い感じに運用できています。
大体のフレームワークにはマイグレーションが標準機能やプラグインとして提供されていますので出番は少ないかもしれませんが、「あまり合わないので代替えのものを使いたい」という時や「オレオレフレームワークやベタで書いていたりして、そもそも付いてない」という時に選択肢の一つに入れてみてはいかがでしょうか。
Composerで入れられますので、どんな環境でもスムーズに導入できるはずです。

現場からは以上です。

  • このエントリーをはてなブックマークに追加
  • Evernoteに保存