Migrationsプラグインの実践的運用
CakePHP Advent Calendar 2011
CakePHP Advent Calendar 2011 : ATND 20日目の記事です。
19日目:akiyanさんCakePHPの「OrderdBehavior」と「TreeBehavior」はマジで使うべき : akiyan.com
イントロダクション
CakeDCが提供しているMigrationsプラグインは、データベースのインクリメンタルなバージョン管理を行うプラグインで、githubでホストされています。
RoR(Ruby on Rails)のMigrationsをリスペクトしているのは間違いなさそう(どこにも書いてないけど)ですが、中身はそれなりに違います。
Migrationsプラグインの詳細な説明は
CakePHP Migrations plugin: easily version and deploy whole applications by Pierre Martin, Cake Development Corporation
を見てください(英語)。
バージョン管理システムとベストマッチ
CakeDCの記事にもある通り、Migrationsプラグインはバージョン管理システムとの協調に大きく貢献します。
これは例えば、開発中はトピックブランチ*1を必ず作って、マージしても良いと判断されたらその大元(masterやdevelopブランチ)へマージする、といった運用がよく行われることと思います。
ここでトピックブランチ内でデータベースのスキーマを変更することは度々あることでしょう*2。
これを、そのブランチの開発者の開発環境外*3で適用するにはどういった方法が考えられるでしょう?
2番目と3番目の方法はあまりにも非効率的で、複数人で開発する時はもはや致命的と言っていいほどの運用でしょう。
スキーマで事足りている、というのならそれもまた是ですが、コールバックにやや不満が残るところです。
独自の方法は情報共有を密にしなければいけない他、学習コストも考えられます。
Migrationsプラグインを使えば、コマンド一発で全てのスキーマバージョンのコントロールが行えるため、スキーマの変更を伴う機能の追加・排除を統一的な手順によって行うことができます。
前提条件
この記事では上記のような、
- ソースバージョン管理システムをgit
- トピックブランチを作る運用
- スキーマバージョン管理システムをMigarations
- DBは便宜的にMySQL
で行うことを前提に、Migrationsプラグインを運用する実践的なTIPSの紹介をします。
バージョンの生成
バージョンとは、Migrationsプラグインが管理するスキーマの変更点を記述するもので、これをインクリメンタル*4に羅列することによって、アプリケーションのスキーマのロードマップが描かれます。
最初のバージョンは、全てのテーブル*5の作成という支持を与えることになります。
手動で全てを書くことももちろん不可能ではないですが、自動生成をすることが必要となってくるでしょう。
データベースのテーブル作成、変更、削除については多種多様なツールがあり(例えばMySQLAdmin)、それらを用いて行うのが通常だと思われます。
その変更点の差分を取ることによって、バージョンの自動生成を行うことになります。
差分を生成するには
cake migration generate
コマンドを叩きます。
基本的にはy, nを入力(テーブルの比較をするか、プレビューをするか)の後、最後にバージョンの名前を指定すると、バージョンが生成されます。
モデルが無いテーブル
デフォルトではモデルがあるテーブルのみが自動生成の対象となります(schemaと一緒ですね)。
これを回避するには、-f(force)オプションを指定します。
cake migration generate -f
Schemaファイルとバージョンの生成
バージョンの自動生成をする際、Schemaファイルがあると(app/config/schema/schema.php)、それと現在のDBスキーマを比較します。
逆にSchemaファイルが無いと、全てのテーブルがバージョンとして生成されます。
つまり、差分をうまく吐くにはSchemaファイルが必要になってきます。
従って、バージョンを吐いた後に、スキーマファイルも(必要であれば上書きして)生成しておく必要があります。
cake migration generate -f cake schema generate -f
さて、最初のバージョンを作成する方法は二通り考えられるでしょう。
1番目の方法を取ると、ある大きな問題にぶち当たります。
それは(例えば本番環境で)最初から一気にスキーマを適用しようとした時、Schemaファイルを実行するしか術が無いということです。
そして、Migrationsのバージョン管理(schema_migrationsテーブル)は、「何も適用されていない」ということになります。
これは次のバージョンアップがある場合、バージョンを適用するだけとはいかず、Migrationsを実行した場合と差異が出る場合があります。
これを避ける為に、2番目の方法を取る必要があります。
従って、以下のフローをお勧めします:
- MySQLAdminなどを使ってテーブルの作成
- Migrationバージョンの生成
- 次に差分を自動するためのSchemaファイルの生成
トピックブランチごとのバージョン管理
トピックブランチでスキーマの変更がある場合、そのトピックブランチ一つごとにバージョンを作成することは良い習慣となります。
トピックブランチを作業ブランチに取り入れた後、スキーマのバージョンを最新にするには以下のコマンドを実行します。
cake migration run all
ここまでは素直なフローですが、あるトピックブランチで新しいバージョンを適用した後、そのトピックブランチが適用されるまでに、他のトピックブランチで作業をしたい場合もあるかと思います。
そこでスキーマバージョンのダウングレードを行います。
cake migration run down
例えば上記を実行することでこれは達成できます。
注意することは、トピックブランチを移動する前にこれを実行しなければならないということです。
移動した後では、差分の情報があるファイルが失われてしまい、ダウングレードが正常にできないためです。
コールバックを用いた不整合の起きないバージョンの作成
Migrationsプラグインの大きな特徴はコールバックの整備です。
コールバックをふんだんに使うことによって、うまく整合性を保つことが運用において最重要となります。
初期レコード
Migrationsを使う場合、初期レコードの生成・更新はバージョンのコールバックを使って行うべきです。
Schemaでもこれは行えるのですが、バージョンごとに管理できる分だけMigrationsならリリースごとに初期レコードを挿入できる分非常に便利です。
初期レコードの更新を行う方法は様々ですが、レコードを配列でもっておき、それを挿入・更新するような実装になっていれば良いでしょう。
上記(抜粋)は一例です。
プライマリーキーの変更
静的なテーブルで、プライマリーキーを例えば整数からUUIDに変更したい場合*6、関連するモデルの外部キーも変更したいところです。
これを行うにあたって、InnoDBで外部キー制約をつけている場合、外部キー不整合が起きる為、エラーが起きることがあります*7。
これを回避するために、そもそも外部キー制約をしないというのがひとつの方法となり得ます。
しかし、既に外部キー制約を用いたセットアップをしてある場合は一筋縄ではいかないでしょう。
そこで外部キー制約を一旦削除して、レコードの更新などを行なってから元に戻す、ということが必要になります*8。
例の抜粋をあげます:
汎用化してる為見難いですが、やってる事自体は単純です!