【CakePHP】お手軽便利なCakeSchema
DBのテーブル設定は非常に面倒な作業の一つです。
特に、開発時は仕様変更などでテーブル内のフィールドが頻繁に増減することもあるかもしれません。
テーブルを作成したり、更新したりするのに、皆さんはどのような手順を踏まれるでしょうか?まずSQLを書いて、アップロードして、mysqlやpsqlのコンソールを使って実行していますでしょうか?それとも、mysqladminとかのguiツールを使っていますでしょうか?
CakePHPには、schemaシェルが付属されていて、これを用いることで簡単にテーブルを初期化することができます。コマンドラインからコマンド一発で(実際には確認メッセージがあるのでy/n選択がありますが)、書き換わるので大変に便利です。
ただ、ドキュメントや情報が公開されているブログなどが少ないため、どのように記述して良いか分からない方も多いかと思います。そこで、簡単に使い方を解説し、今回の目玉である「初期値を登録する方法」も伝授しちゃいます。
■schemaシェルの使い方
schemaシェルは、cakeコンソール内から呼び出されます。基本的な使用方法は次の通りです。
cake schema [-app (app名)] run create [(スキーマ名)] [(テーブル名)]
スキーマ名が無指定だと、AppSchemaが使われ、テーブル名が無指定だと全てのテーブルを処理します。
ちなみに、最初の「y/n/q」は「現在あるテーブルをドロップするか?」の問い合わせ、次が「新しくテーブルを作るか?」の問い合わせです。
また、現在設定されているテーブルを解析して、AppSchemaを作ることもできます。
cake schema [-app (app名)] generate [-f]
なお、AppSchemaはschema.phpとして、「app/config/sql」に保存されます。
自前で作成する場合も、基本的にはここに作成します。
また、標準では2回目以降は、スナップショットをとるか聞いてきますが、「-f」オプションをつけるとオーバーライトします。
※ほかにもオプションなどがありますので、詳しくはusageをご覧ください。
■schema.phpの記述方法
schema.phpは、次のようなクラスを記述します。
<?php
class AppSchema extends CakeSchema {
var $name = 'App';
//beforeコールバック
//
function before($event = array()) {
return true; //正常終了はtrueを与える
}
//afterコールバック
//
function after($event = array()) {
}
//各テーブル(この場合はusersテーブル)
//
var $users = array(
'id' => array(
'type' => 'integer', //型(int型)
'null' => false, //nullがOKか?(falseなので不許可)
'default' => null, //デフォルト値(null)
'key' => 'primary', //インデックス(primaryを指定)
'extra' => 'auto_increment', //auto_increment指定
'length' => 10, //型の長さ(int(10))
),
'created' => array(
'type' => 'datetime',
'null' => false,
'default' => null,
),
'modified' => array(
'type' => 'datetime',
'null' => false,
'default' => null,
),
'name' => array(
'type' => 'string', //string = varchar型
'null' => false,
'default' => null,
'length' => 255, //= varchar(255)
),
'indexes' => array(
'PRIMARY' => array(
'column' => 'id', //対象カラム
'unique' => true, //ユニークなインデックスの場合はtrueを
),
'user_name_idx' => array(
'column' => array('id', 'name'), //カラムが複数の場合はarrayで
'unique' => true,
),
),
);
}
型は、データベースの種類によって違いますが、おおむね次のような型を指定できます(下記はmysqlのもの)。
各DBのdatasourceの最初の方に定義されていますので、お使いのDBでどんな型が使えるか、確認してみてください。
string => varchar
text
integer => int
float
datetime
timestamp
time
date
binary => blob
boolean => tinyint
定義されていない型は、string(varchar)と見なされるようです。
※これはdatasource由来のものなので、schemaだからということはありません。
■初期値を簡単に与える方法
さて、ここまではソースを参照すれば何となく分かる範囲だと思いますが、ここからはECWorksオリジナルのTipsです。
このようにしてテーブルの初期化はできますが、マスタ(定数)を登録しておきたい場合もあると思います。毎回値を手入力するのは大変ですよね。
そこで、schema.php内にある「after()」コールバックで定義してしまおう、というのが今回の趣旨です。
しかし、一つ困った点があります。それは「schema.php内に記述したプロパティは、全部テーブルを作るのに使われてしまう」のです。さらに困ったことに、ご丁寧にも「テーブルとして登録したデータは、プロパティから削除されてしまう」のです。つまり、schema.php内に、定義したいデータを書くことができないのです。
そこで、「schema.phpではない場所にデータを書いて、schema.phpから呼び出してしまう」ことで、上記問題を解決してしまいます。
まず、after()コールバックに、次のようなコードを記述します。
function after($event = array()) {
//初期値ツールの呼び出しと実行
//
if(!empty($event['create'])){
if(!isset($this->InitialValues)){
require_once($this->path.DS.'initial_values.php');
$this->InitialValues = new InitialValues();
}
$modelname = Inflector::classify($event['create']);
$this->InitialValues->set($modelname);
}
}
これは、テーブルのcreate時にのみ、InitialValuesというクラスを呼び出し、$event['create']に書かれているテーブル名からモデル名を作成し、そのモデルを呼び出してデータ登録を行います。
そして、InitialValuesクラスは次のように記述します。
<?php
class InitialValues extends Object {
//各モデルの初期値
//
var $values = array(
'User' => array(
array(
'name' => '麻生',
),
array(
'name' => '鳩山',
),
array(
'name' => '志位',
),
array(
'name' => '綿貫',
),
array(
'name' => '福島',
),
);
//初期化処理
//
function startup($schema = null){
if($schema === null){
return false;
}
}
//セット関数
//
function set($modelname){
if(!empty($this->values[$modelname])){
$this->{$modelname} = ClassRegistry::init($modelname);
$this->{$modelname}->saveAll($this->values[$modelname]);
}
}
}
今回はUserしか書いていませんが、複数テーブルがある場合は同階層で列挙していきます。
欠点は、model名とテーブル名をcake規約で書いていない場合、このやり方ではうまくいきません。別途テーブルを作るとかの改造が必要です。
また、配列を工夫すれば、アソシエーション付きのデータを登録することもできると思います。
以上で簡単な説明になりますが、コマンドラインが使え、なおかつ複雑なテーブル定義をしない(Cakeで一般的とされているテーブル定義で事足りる)場合は、上記手法はなかなかおすすめかと思います。
なお、現時点のバージョン(1.2.4)では、エラーがコールバック内で参照できないため、その点も注意が必要かもしれません。
schemaはまだまだ発展途上の機能かと思われますが、それは大勢の方に使われていないからだと思います。この情報を公開することで、schemaの利用が活発になり、よりよいものになってくれればと期待します。





テーブルに対応するModelを未作成の状態で、複数のtableに初期値を流し込もうとしてちょっとはまりました(^^;
うまい方法が見つからなかったので、強引に
$this->{$modelname}->_schema = null;
とかして逃げました。もっとスマートな方法はないのかなぁ(^^;。
あーなるほど、modelがないとうまく動かないかもですね。
bakeでも何でも良いので、初期値を簡単に入れられる仕組みが確立されると良いんですよねー
「SQLで良いじゃない?」と言われるかもしれないのですが、逆にSQLだと困るケースもあるので。
CakeScehmaが今後発展することを祈りたいです。
ちなみに、プロパティ全部がSchema内部のテーブル情報に食われてしまう問題は、どうやら1.3で改善される方向で進んでいるようです。チケット投げて良かった!
まぁ,空のModel作っちゃえば良いんですけどね(^^;。
既に動いているプロジェクトは、sqlでやっていましたが何かと不便だったのでお試し中です。
なかなか良いですよね。
> ちなみに、プロパティ全部がSchema内部のテーブル情報に食われてしまう問題は、どうやら1.3で改善される方向で進んでいるようです。チケット投げて良かった!
おぉ!すばらしい!
1.3は大きな改変はないようですが、細かいけど便利な機能が増えそうで楽しみですね!
SQLの方が確かに細かいことが出来るのですが、プログラムで扱えることになるので幅が広がりますよね。
1.3がどういう方向で進化するかはまだ見えていないのですが、より便利になると良いですね。