CakePHPでエンティティを使えるプラグイン - CakeEntity
CakePHPのモデルはデータにアクセスするためのオブジェクトです。モデルが本来記述しているデータは単に配列として表現されています。他言語のメジャーなフレームワーク、RailsやDjangoと比較してCakePHPが大きく違っている部分であり、CakePHPがディスられる大きな要因の一つとなってるように思います。
O/Rマッパーによってデータソースのレコードと結びつけられたオブジェクトは、一般的にエンティティと呼ばれます。用語的には永続化されたオブジェクトぐらいに考えて良いかと。エンティティはオブジェクトなので、当然オブジェクト指向的にも相性がよく、好んで使われる手法です。
CakePHPのモデルを使った場合と、エンティティを使った場合のコーディングとを比較すると以下のように違いが出ます。
CakePHPでの保存のコード
<?php $Post = ClassRegistry::init('Post'); $data = array('Post' => array( 'title' => "Hello world", 'author' => "Basuke", )); $Post->save($data);
エンティティが使えた場合のコード(仮想的なコードです)
<?php $post = new Post(); $post->title = "Hello world"; $post->author = "Basuke"; $post->save();
軽くすっきりしているのがわかると思います。
僕は圧倒的にエンティティ派です。オブジェクト指向的に、せっかくエンティティとして表現できるものを単なる配列にしてしまうCakePHPのこの辺の仕様にどうにも納得できていません。なので昨年末から、オレオレのエンティティ対応のモデルレイヤーを作っていますが、なかなか完成できませんw。やっぱりCakeのモデルの、データアクセスレイヤーとしてはよくできています。全部作り直すのは無理があるのでは、という考えに落ち着いてきました。
というわけで方向転換、あるものは積極的に使おうということで、CakePHPのモデルはそのまま使ったまま、返ってきたデータを簡単にオブジェクトにすればいいんじゃないか、という方針でプラグインを作ってみました。
ファイル3つ(実質的に2つ)のシンプルなプラグインですが、けっこう使えます。通常のモデルの機能には全く影響を与えずに、エンティティとして取得したい場合だけエンティティにしてくれるという、奥ゆかしいやつです。
CakeEntityの使い方
使い方は簡単です。まずプラグインをアプリに追加します。名前はentityにしてください。
$ git submodule add git://github.com/kanshin/CakeEntity.git app/plugins/entity
次に、エンティティとして取得できると便利と思われるモデルの親クラスを、AppModelからEntityModelに変更します。App::importも必要なので追加してます。
<?php App::import('Model', 'Entity.EntityModel'); class User extends EntityModel { ...
EntityModelはAppModelを継承してますので、AppModelの機能はそのまま使い続けられます。
最後に、findに渡すパラメータに 'entity' => true を追加します。
<?php ... $users = $this->User->find('all', array( 'conditions' => array('is_hidden'=>0), 'order' => 'modified', 'entity' => true, )); ...
これで$usersにはユーザーの情報を収めたEntityクラスのオブジェクトの配列が返ります。なのでビューでforeachなどで気軽に使うことが出来ます。
<?php ... foreach ($users as $user) { echo h($user->nickname); }
ただし、この時点では$userは汎用的なEntityクラス(EntityModelクラスではありません)のオブジェクトです。これだけだと配列のカギ括弧が少なくてすっきり!ぐらいの意味にしか見えないでしょう。
EntityModelがデータをエンティティ化するさいに、モデル名+Entityというクラスが定義されていたら、そちらを使うようになってます。モデルファイルの上に定義してみましょう。
<?php App::import('Model', 'Entity.EntityModel'); App::import('Model', 'Entity.Entity'); class UserEntity extends Entity { // ユーザーのURLを返す public function url() { return Router::url(array('controller'=>'users', 'action'=>'view', $this->id)); } } class User extends EntityModel { ... }
こうなると、先ほどの$user変数にはUserEntityのインスタンスが入ります。$user->url() でビューからでも簡単に複雑なロジック(この場合簡単ですがw)を呼び出すことが出来るのです。
配列にしてしまうと、何のデータだったのかと言う大事な情報が失われてしまいますが、エンティティであればクラスという言語ネイティブな形で情報が維持されます。これはすごく大事なポイントです。
今日はここまでです。詳しい使い方は、続きを書きます。興味ある方はぜひ使ってみて感想を聞かせてください。
さあ、これでRailsの人から後ろ指さされることもなくなりますよー(たぶん)。