このチュートリアルは、前回で一旦リリースを迎えて
今回から新機能の実装と、バグの修正などを行っていくという
フェイズに入りました。

そこで、改善する前にひとつ、大きな問題があります。
それはデータベースモデルです。

リリース後にユーザによってインストールされ、その時に作成されたテーブルを変更することは
利用者にとっても、開発者にとってもリスクの伴う作業となるでしょう。

そこで、OpenPNEではテーブルに変更がある時に、プラグインごとのリビジョン番号のついた
「マイグレートスクリプト」を作成し、マイグレートスクリプトに付けられた
リビジョン番号によって、データベースの状態の管理を行います。

変更計画

まずは、変更計画を考えなくてはなりません。

3. プロジェクト内で後半部で対応する機能の中に、公開範囲の設定という項目があります。

現行のモデルでは対応することはできませんので、
以下の事を行うマイグレートスクリプトを用意することにします。

  • VoteQuestionへ、public_flagフィールドを追加

新データモデルをschema.ymlへ書き出す

マイグレートスクリプトを書き出す前に、変更後のモデルなどを
schema.ymlに書き出しておきます。
なぜ、これを最初にやるかというと、マイグレート実行時は、テーブルの変更前に
モデルクラスなどの更新を行うためです。
つまり、テーブルの内容の書き換えなどをマイグレートスクリプトで行う事を
想定すると、先にこのファイルを変更しておかないと整合性が取れなくなってしまいます。

plugins/opVotePlugin/config/doctrine/schema.yml の一部

VoteQuestion:
  actAs: [Timestampable]
  columns:
    id: { type: integer(4), primary: true, autoincrement: true }
    member_id: { type: integer(4), notnull: true }
    title: { type: string(140), notnull: true, notnull: true}
    body: { type: string }
    public_flag: { type: integer(1), notnull: true, default: 1 } 
  relations:
    Member: { onDelete: cascade }

マイグレートスクリプトを作成する

マイグレートスクリプトはプラグインの data/migrations
に設置します。さらに、そのディレクトリ下はバージョンごとに分けることができ
data/migrations/バージョン/ に置くことが望ましいでしょう。

opVotePluginは、次回バージョンを 0.9.1 としたいので

plugins/opVotePlugin/date/migrations/0.9.1/

を作成します。
その下に、リビジョン番号(三桁)_スクリプト名.php
というファイルを作ります。

今回は、このプラグインにとって初めてのマイグレートスクリプトなので、リビジョン番号は001です。
スクリプト名は任意のものにしてください。
今回は以下の名前を使うことにします。

plugins/opVotePlugin/date/migrations/0.9.1/001_add_public_flag_column.php

スクリプトの内容は、Doctrineのマイグレーションに利用するものと同じです。
以下を確認してください。

Doctrine – Chapter 24 Migrations

今回はカラムを追加するだけで済むので以下のようになるでしょう。

<?php

class opVotePlugin1_AddPublicFlagColumn extends Doctrine_Migration_Base
{
  public function migrate($direction)
  {
    $this->column($direction, 'vote_question', 'public_flag', 'integer', 1, array(
      'notnull' => true,
      'default' => 1
    )); 
  }
}

このとき、マイグレートスクリプトごとに1クラスとなりますが、
他のマイグレートスクリプトで利用しているクラス名とコンフリクトすると非常に厄介な事になります。

そこで、プラグイン名のあとにリビジョン番号_マイグレート名 (opOpenSocialPluginの場合)
単に、プラグイン名Versionリビジョン番号 (opDiaryPluginの場合)
というクラスの命名方法が良く使われています。

新規インストール者のための準備

さて、ここで新規インストールを行うと、上のschema.ymlで設定された内容によって
データベースがセットアップされます。
しかし、このままではリビジョン番号を記録していないので、上で用意したマイグレートスクリプトが
実行されてしまいます。

そこで、セットアップ時に挿入されるfixturesを使ってリビジョン番号を最初から
挿入することにします。

plugins/opVotePlugin/data/fixtures/000_revision.yml

SnsConfig:
  opvoteplugin_current_revision:
    name:  "opVotePlugin_revision"
    value: 1

見て分かる通り、リビジョン番号はSnsConfigで管理されます。
このファイルはマイグレートスクリプトを作成するごとに更新することを忘れないようにしてください。

マイグレートテスト

テストで繰り返しマイグレートスクリプトを実行したいとき、ブランチを作成してmasterと作成したブランチとの間を行き来する方法が有効と言えるでしょう。
プラグインのディレクトリ下で

$ git checkout -b migrate1 

と、「migrate1」ブランチを現在のブランチ(master)から作成し、移動するコマンドを実行します。

$ git add .
$ git commit -m "added migrate script"

今までの変更をすべて追加し、コミットします。

マイグレートスクリプトの実行の前に
比較のため、これまでの状態のDBの状態を確認しておくと良いでしょう。


mysql > DESC vote_question
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment | 
| member_id   | int(11)      | NO   | MUL | NULL    |                | 
| title       | varchar(140) | NO   |     | NULL    |                | 
| body        | text         | YES  |     | NULL    |                | 
| created_at  | datetime     | NO   |     | NULL    |                | 
| updated_at  | datetime     | NO   |     | NULL    |                | 
+-------------+--------------+------+-----+---------+----------------+

マイグレートを実行します。以下のコマンドをOpenPNEのディレクトリで行ってください。
このとき、–targetオプションと、–no-update-pluginオプションを指定します。
–targetは指定したプラグインのみのマイグレートを行うためのものです。他のプラグインの影響を避けることができます。
–no-update-pluginは、マイグレート時に行われるバンドルプラグインの同期作業などを省略することにより、実行時間を短縮するために付加します。

$ ./symfony openpne:migrate --target=opVotePlugin --no-update-plugin

mysql > DESC vote_question
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment | 
| member_id   | int(11)      | NO   | MUL | NULL    |                | 
| title       | varchar(140) | NO   |     | NULL    |                | 
| body        | text         | YES  |     | NULL    |                | 
| created_at  | datetime     | NO   |     | NULL    |                | 
| updated_at  | datetime     | NO   |     | NULL    |                | 
| public_flag | tinyint(4)   | NO   |     | 1       |                | 
+-------------+--------------+------+-----+---------+----------------+

マイグレート成功です!

成功したので、masterにmigrate1ブランチを取り込みましょう。

$ cd plugins/opVotePlugin
$ git checkout master
$ git merge migrate1

失敗した時

マイグレートスクリプトを見直し、もう一度やりなおします。
この状態のとき、ブランチを切り替え、データベースを作りなおすことにより
前の状態に戻ります。

$ git checkout master
$ cd ../../
$ ./symfony doctrie:build --all --and-load

マイグレートスクリプトを書きなおすために、先程まで作業していたブランチに
戻ります。

$ cd plugins/opVotePlugin
$ git checkout migrate1

マイグレートスクリプトを書き直し、再びマイグレートを実行してテストしてください。

マイグレート時にデータを挿入する

今回は、実際には行いませんがマイグレートスクリプトの実行時に
データを挿入することもできます。

その方法は、非常に簡単で、マイグレートスクリプトを用意し、
そのスクリプトのあるディレクトリ下に fixtures/ ディレクトリを作成します。
その中に

リビジョン番号_マイグレート名.yml

というymlファイルを用意し、初期データ用のフォーマットと同じように
データベースに挿入する項目を用意してやるだけです。

リスク

素晴らしいマイグレートスクリプトを用意しても、データベースの構造を変える以上
データを破壊してしまったり、正常に動かなくなるというリスクを伴います。
できるだけデータベース構造を変更しない設計をするのはもちろんですが、
安定版・開発版という別々のバージョンをリリースするのもひとつの手でしょう。

尚、OpenPNE3本体では、データベース構造の変更は原則として開発版でしか
行わないというルールが存在します。