Logicky Blog

Logickyの開発ブログです

cakePHP こわいRecursive

http://book.cakephp.org/2.0/ja/models/model-attributes.html

recursiveは恐ろしい。怖い人はAppModelに、public $recursive = -1;と書いておけばデフォルトで-1になるようだ。 これをしない場合、デフォルト値は1になる。

  • recursive -1 は、JOINしない。自分だけとってくる。
  • recursive 0 は、自分と自分がbelongsToしているモデルだけとってくる。
  • recursive 1 は、自分とbelongsToとHasManyをとってくる。
  • recursive 2 は、自分とbelongsToとHasManyと、BelongsToとHasMany先が関連しているモデルを一階層分とってくる。

みたいな感じになっているようだ。0は自分だけという意味ではないってことである。それにしても0と1と2はいまいち明確な違いが分からない。

ブログみたいなシステムを考えて、UserがPostを投稿し、PostにCommentがつき、PostにはTagも設定できるようにしてみる。この場合、UserはPostをHasManyしていて、PostはUserにbelongsToしている。PostはCommentをHasManyしていて、CommentはPostにbelongsToしている。また、CommentはUserにもbelongsToしている。また、PostとTagはHABTMになっている(PostはPostsTagをHasManyしている)。

Postをrecursive0でfindすると、PostとUserが得られる。Commentは得られない。なのでやはりbelongsToは得られるが、HasManyは得られないのだ。

Postをrecursive1でfindすると、PostとUserとCommentとTagが得られる。Tagの中にPostsTagが入っている。こういうことを自動的にやってくれているらしい。確かに1の場合はHasManyもとってくるのだ。ただし、HABTMの場合は、中間テーブルを取ってくるのではなく気を利かせてHABTM先のモデルを取ってきてくれるらしい。

recursive2でfindすると、Userの中にPostが入っている。Commentの中にはUserとPostが入っている。なので下手にrecursiveをあげるとすごいことになる。

recursive -1 , 0 までは1クエリで、recursive1で、3クエリ。でもrecursive2で、32クエリ。3で、113クエリ。4で、309クエリ。5で865クエリ。6で2228クエリ。recursive2以上を使う場合は、bindModel()とunbindModel()を使って都度アソシエーションを限定的にする必要がある。

http://d.hatena.ne.jp/takuya15/20071219/1198035692 これは、都度動的にアソシエーションを設定することを簡単にする方法が書いてある。これは結構いいかもと思った。

$this->Post->recursive = 2;
$this->User->unbindModel(array('hasMany' => array('Post', 'Comment', 'Item')));
$this->Comment->unbindModel(array('belongsTo' => array('Post')));
$posts = $this->Post->find('all');

こうやって動的にunbindModelを使うと、Commentした人の名前だけ4クエリで取得できるからrecursive2以上を使うときはこういうことをしないといけない。

全部モデルにアソシエーションは書いておいて、AppModelでrecursiveのデフォルト値を-1に設定しておいて、基本recursiveを使わないようにしつつ、どうしても必要なときだけ、unbindModelを使うっていう感じでいいかのう。

と思ったらなんかおかしい。commentの件数分、個別にUserをクエリで取得しているようだ。なんだこれは。100件コメントがあったら100クエリするつもりのようだ。CommentにUserをJoinすればいいだけの気がするが。

http://d.hatena.ne.jp/hetima/20070225/1172409266にも同じようなことが書いてある。でもこれは、そもそもrecursive0でよかったのに2にしていただけともいえると思った。 それにしてもやっぱりこわい。HasManyされているやつが、何かにbelongsToしていて、その何かがrecursive2以上に該当する場合は注意が必要だということであります。

まあ要するに効率的にやろうとすると、cakePHPも都度色々コードをしっかり書かないといけないってことかのう。