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がどういう方向で進化するかはまだ見えていないのですが、より便利になると良いですね。