PHPMigrate - PHPでマイグレーションツールを作った
デプロイツールに引き続き、今度はPHPでMigrationツールを作りましたので、記事を書きます。
PHPMigrate - https://github.com/kohkimakimoto/phpmigrate
フレームワークとMigration
最近のWebアプリケーションフレームワークを使えば、まあだいたいMigrationの機能ついてくるんですよね。でもフレームワークに組み込まれているものだと、Migrationを実行するためにアプリを実行環境にデプロイしないとならなかったりします。Migrationとメインのアプリのコードがひとつのプロジェクトとしてフレームワークで規定された構造にパッケージされているので、Migrationのコードだけデプロイするというのはなかなか難しいものがあります。
Migrationで変更するDBのスキーマは実運用上だいたいカラムの追加やテーブルの追加がほとんどで、カラムにデフォルト値などを適切に設定しておけば、アプリのコードのデプロイより先行してDBのスキーマを変更しても問題ない。
けれどスキーマ変更より先にコードがデプロイされると、存在しないテーブルやカラムを参照してしまいエラーを起こしてしまいます。
デプロイ作業的には
DBスキーマ変更(Migration)→アプリのコードをデプロイ→キャッシュ削除などの後処理。
という順序でいきたいのだけど
アプリのコードをデプロイ→DBスキーマ変更(Migration)→キャッシュ削除などの後処理。
というようになってしまいます。これを避けるためには、Migrationだけを行う専用の実行環境を用意したりと、ちょっと工夫したりしなければならない。
フレームワークにMigrationが組み込まれているとDBの接続情報をアプリとMigrationで一元管理ができて、いいこともあるのだけど、個人的にはMigrationはフレームワークに対して、もうちょっと疎結合に構成されていたほうがいいんじゃないかという考えに至りました。
PHPMigrate
そういうわけで、すごくシンプルなMigrationツールをつくりました。
https://github.com/kohkimakimoto/phpmigrate
使い方
2013/04/22 追記
複数のDBに対してMigrationできるように修正したので、この記事の記述とは設定の書き方が変わりました。READMEを参考にしてください。
プログラムはmigrate.php
というPHPファイル一つだけなので、これをMigrationのタスクを管理するディレクトリにダウンロードします。ダウンロードしたら、エディタで開きます。ファイルの上の方にDBの接続情報を記述する箇所があるので、適宜環境にあわせて修正してください。
MigrationConfig::set('database_dsn', 'mysql:dbname=yourdatabase;host=localhost');
MigrationConfig::set('database_user', 'user');
MigrationConfig::set('database_password', 'password');
MigrationConfig::set('schema_version_table', 'schema_version'); # migarationの版を管理するテーブル名
修正したら
php migrate.php create [任意のマイグレーションタスク名]
というコマンドを打つと、
[タイムスタンプ]_[任意のマイグレーションタスク名].php
でPHPファイルが生成されます。
<?php
/**
* Migration class.
*/
class Dummy
{
public function preUp()
{
// add the pre-migration code here
}
public function postUp()
{
// add the post-migration code here
}
public function preDown()
{
// add the pre-migration code here
}
public function postDown()
{
// add the post-migration code here
}
/**
* Return the SQL statements for the Up migration
*
* @return string The SQL string to execute for the Up migration.
*/
public function getUpSQL()
{
return "";
}
/**
* Return the SQL statements for the Down migration
*
* @return string The SQL string to execute for the Down migration.
*/
public function getDownSQL()
{
return "";
}
}
そして、getUpSQLメソッドの戻り値にテーブル追加のSQL、getDownSQLにそれをもとに戻すSQLが戻されるようにコードを修正します。以下のような感じです。
public function getUpSQL()
{
return <<<END
CREATE TABLE `sample` (
`id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`id`) )
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_bin;
END;
}
public function getDownSQL()
{
return <<<END
DROP TABLE `sample`;
END;
}
テーブルの変更内容はSQLを直接書きます。SelectやInsertを抽象化してくれるORMは好きなのですが、テーブル定義を行うDDLのようなSQLはRDBMSの実装の影響も強く受けやすいと思っているので、無理に抽象化しない方針で設計しました。このほうが細かい調整がしやすいし、DBが最終的にどういう定義になるのかがわかりやすいと思っています。
あとは、以下のコマンドでmigrationを実行します
php migrate.php migrate
これで、変更が実行されます(上記の例だとsampleというテーブルが作成される)。そしてMigrationの版管理をするschema_versionというテーブルが自動で作成されてそこにファイルのプリフィクスだったタイムスタンプが登録されます。
戻す場合は
php migrate.php down
で戻せます。また他のコマンドとして
php migrate.php up # 複数あるマイグレーションタスクで1件だけ進ませる。
php migrate.php status # 現在の状態の表示。現在の版と、未実施のマイグレーションの一覧を表示。
があります。
またDB定義を書くのに、プログラムであるmigrate.php
を直接修正したくない場合は適当なPHPファイルを作って以下のようにmigrate.phpを読み込めば、定義情報を外出しすることもできます。
<?php
require 'migrate.php';
MigrationConfig::set('database_dsn', 'mysql:dbname=yourdatabase;host=localhost_modify');
MigrationConfig::set('database_user', 'user_modify');
MigrationConfig::set('database_password', 'password_modify');
MigrationConfig::set('schema_version_table', 'schema_version_modify');
Migration::main();