Cakephpのモデルはフレキシブル
Cakephpでは、仮にデータベース側のデータ構造に十分でない点があってもCakephpのモデルで柔軟にカバーできる機能が複数備わっている。モデルの主キーを操作する関数だったり、モデルのアソシエート先を変更する機能を活用して関係するデータの構成することができる。
以下はCakephpでページネーションを構成する処理。Productモデルには、Counterモデルとhasoneのアソシエーションが設定されている。つまり、
Product(商品)は、Counter(カウント)を持つ
という関係がCakephpのモデルレベルで定義されている。なお、ProductとCounterがidという共通のフィールドで連結されるものと想定している。
$this->paginate = array( 'page'=>1, 'conditions'=>array('Product.status' => '1'), 'sort'=>'datetime', 'limit'=>20, 'order' => array('start_datetime' => 'desc'), 'direction'=>'desc', 'recursive'=>0 ); $data = $this->paginate();
上のコードによりモデルから20件のデータを取得できる。ProductとCounterはidフィールドで1対1で対応しているので、それぞれの商品とカウンタが対応したデータ構造が$dataに格納される。ここまではCakephpの通常のページネート処理である。
primaryKeyとbindModelでデータベースを異なるレイヤーで変更してしまう
システムやサービスの拡張によりShopモデルが新設され、Productモデルと連結させる必要が生じた場合を考えてみたい。連結させたいがCounterの場合と異なり、idという共通のフィールドをProductとShopが共有していない場合を想定している。
その場合には、主キーを変更し、アソシエート先モデルを動的に変更することで対応できる。具体的には、
$this->Product->primaryKey = 'email';
上の処理で主キーがemailにcakephpのモデルレベルで変更される。ProductモデルとShopモデルはemailというフィールドを共有していることを想定している。そして、アソシエートを構成し直す、
$this->Product->unbindModel(array('hasOne' => array('Counter')), false); $this->Product->bindModel(array('hasOne' => array('Shop' => array('className' => 'Shop', 'foreignKey' => 'email'))), false);
Counterのアソシエーションを解除した後、Shopモデルに対して連結処理を行っている。そのためのモデル関数がbindModelで、cakephpの動的アソシエーションに有効に活用できる機能だったりする。ここまで準備をしたらさきほどと同じpaginate処理を実行する。
$this->paginate = array( 'page'=>1, 'conditions'=>array('Product.status' => '1'), 'sort'=>'datetime', 'limit'=>20, 'order' => array('start_datetime' => 'desc'), 'direction'=>'desc', 'recursive'=>0 ); $data_store = $this->paginate();
paginate処理は同一でも返されるデータ構造はさきほどとは異なり、productモデルとshopモデルで構成されたデータ構造が返される。意図的に返されたデータを先ほどとは異なる変数に格納している。最後にphpレベルでデータの構造を操作し、関係データとして構成している。
$data_store_c = count($data_store); for( $i = 0; $data_store_c > $i; $i++ ) { $data[$i]['Shop'] = $data_store[$i]['Shop']; }
$dataには、productとcounter、そしてshopというモデルで構成された、それぞれが関係するデータ構造を取得できる。
Cakephpには、システムやサービスの拡張や変更に伴い、柔軟に構成を変え対応していける機能が豊富に備わっている。