2012年6月27日水曜日

cakePHP2.1でjoinーbelongsToを使う

今回はbelongsToに関して。多対一の結合で、正規化を実装するものだからおそらくはhasManyと並んで一番使うアソシエーションになるんじゃないかなぁって気がする。

基本はhasOneとほぼ同じ

belongsTo(LEFT)JOINでの結合を行う。これは、実はhasOneの結合方法と同じで、その動きも返される配列の形も殆ど変わらない。
両者の決定的な違いはbelongsToは外部キーを自身が持ち、hasOneは相手(アソシエーション先)が持つということみたい。

アソシエーション外部キー
hasOne相手が持つ
belongsTo自分が持つ

例えばBlogUserBlogというモデルがあるとして、Blogに外部キーがある場合、以下の設定はほぼ同じ動きをする。

//Model/BlogUser.php
<?php
   class BlogUser extends AppModel{
      var $hasOne = 'Blog';
   }
?>
//上記のと以下の設定は動きがほぼ同じ
//Model/Blog.php
<?php
   class Blog extends AppModel{
      var $belongsTo = 'BlogUser';
   }
?>

なお当然ながら、belongsToを使う場合も自分のと結合テーブル両方のモデルを作成しておく必要がある。

belongsToの宣言

宣言も基本的にhasOneと変わらない。非常に便利なパラメーターがひとつあるだけ。

//app/Model/Blog.php
<?php
  class Models extends AppModel{
     public $hasOne = array(
        'JoinTable'=>array(
            'className' => 'JoinTable',
            'foreignKey'=> 'model_id',
            'type'=>'inner',
            'conditions'=> array('JoinTable.id'=>1),
            'fields' => null ,
            'order'=> null ,
            'dependent'=> true,
            'counterCache' => true ,
            'counterScope' => null
         )  
     );
  }
?>

■キー名(Blog)
この場合、エイリアス名となる。今回はBlogと書いてるけど、当然Bでも何でもいい。

■className
結合するモデル名。上のキー名がモデル名と一致してる場合は省略できる。

■foreignKey
結合先が持ってる外部キー。命名規約に沿ってる場合は省略可能。

■type
結合タイプをleftrightinnercrossの中から選べる。

■conditions
find()で指定する以外に、範囲を制限する場合に使用する。

■fields
抽出するフィールドを選択する。全部を選択する場合、nullにするかこのキー自体を省略する。
逆に全部いらない場合はfalseと設定する。

■order
結合した際にソートする場合に指定する。
hasOneの場合は、find()時でも設定してる場合そのに着く。ASCと書くと昇順DESCと書くと降順となる。

[補足]
あまりSQLに詳しくない人もいると思うので補足すると、前に着くか後に着くかは結構重要になる。
SQLの場合、ソートは記載された順から優先的にされる。
たとえば[BlogUser.id asc]が先に設定されていて、hasOneで[Blog.id desc]と設定した場合、最初にBlogUser.idで昇順ソートされ、BlogUser.idが同じ場合はBlog.idで降順ソートで返される

■dependent
trueとすると、選択モデル(この場合はBlogUser)のフィールドが削除された時それと同じ外部キーを持つ結合モデルのフィールドも削除してくれる。
デフォルトはfalse

便利なcounterCache機能

上記のまではhasOneと同じなんだけど、belongsToでは他にとても便利そうなcounterCacheという機能がサポートされてる。これを使うと、同じ外部キーIDを持つフィールドが何件あるか相手モデル(アソシエーション先)に保存させることができる。

たとえばブログの投稿内容(Blog)とコメント(Comment)を関連付けさせる場合、コメント数が何件あるかをキャッシュさせることができるってことだね。

■使う前の準備
belongsToで結合するアソシエーション先(外部キーがない方)に、以下のようなフィールドを追加する。

選択モデル(アンダースコア型)_count
(例)comment_count int(11) unsigned not null

あとはbelongsTocounterCacheを設定するだけ。

■counterCache
trueとすると、上記の命名規約に沿ったフィールドに、追加、編集、削除の度に数値を増減させてくれる。
また、'counterCache'=>'フィールド名'と設定することもできる。

■counterScope
カウンターキャッシュを増減させる際、このパラメーターで条件を設定できる。
(counterCache版のconditionsみたいなものかな)
たとえば'counterScope'=>array('Model.field' => 1)というような感じで使う。

【簡単な例】
//commentsテーブルにblog_id(外部キー)がある
//blogsテーブルにcomment_countがある
class Comment extends AppModel{
   public $belongsTo = array(
     'Blog'=>array('counterCache' => true)
   );
} 

これだけで、Commentモデルを操作(挿入/編集/削除)するとBlogモデル(アソシエーション先)の「comment_count」が増減するようになるみたい。

これに関して、色々なパターンで検証してみたいからまた別の機会で個別でまとめよかな。ひとまず、色々調べてる時に見つけた紹介されてるサイトをいくつか貼っておきます。

HappyQuality [CakePHP]counterCacheがすごく便利なのでメモ
WapBox [CakePHP]CakePHP - counterCacheについて
半年前の私への教科書 counterCache (HABTMでも)
Ks web Design 手動によるカウンターキャッシュの更新

省略して宣言する場合

belongsToでも、命名規約に沿ってたら当然省略して宣言できます。
あとbindModelももちろん使えるよ。

public $belongsTo = 'Model';
//または
public $hasOne = array('Model1','Model2',...);
複数のテーブルを結合

belongsToは基本的に外部キーを自分が持ってるっていうだけで、あとは「cakePHP2.1でhasOneを使う」と殆ど同じなんで、そこを見てね。

面倒くさがりやさんはこちら


まあ、要はこっち側はhasOneと殆ど同じような感覚で実装できるってことですね。
DBを扱う時、自分が外部キーを持ってる方のテーブルをメインで扱うことが多いから、hasOneよりはこっちの方がよく使うことになりそう。

次回はcakePHPで一番便利そうなhasManyを取り上げてみます。

0 件のコメント:

コメントを投稿