CakePHP

CakePHP group byとinner joinでhasManyデータの件数を検索する

例えばusersテーブルとpostsテーブルがあり、「users hasMany posts」としたとき、ユーザの投稿したメッセージ件数の一覧表など作りたいことなど、普通にあると思う。そんなとき、usersテーブルとposts テーブルをJOINして、同じuser_idを持つpostsデータをGROUP BYでまとめて、count()で件数を引っ張り出す。SQLで書くと以下のような感じ。

SELECT User.id,User.name,count(Post.user_id) AS num FROM users AS User 
LEFT JOIN posts AS Post ON User.id=Post.user_id
GROUP BY User.id;

ここで、投稿を1度もしていないユーザを省略したい場合は、「LEFT JOIN」を「INNER JOIN」に変更すればよい。

さて、この自分が意図するSQLを、どのようにCakePHPを利用すれば、吐き出してくれるのだろうか?

以下のような方法で何とか解決したが、ちょっとトリッキーなやり方だな、と思う(ノ_-;)ハア…。

$this->User->unbindModel(array('hasMany'=>array('Post')));
$this->User->bindModel(array('belongsTo'=>array('Post'=>array(
  'className'=>'Post',
  'foreignKey'=>false,
  'conditions'=>'User.id=Post.user_id',
  'type'=>'inner',
  'fields'=>array('count(Post.user_id) as num')))));

$users=$this->User->find('all', array('group'=>'User.id'));

ここでのミソは、

  • belongsToを利用して「’type’=>’inner’」を利用する
  • JOINの条件は、’foreignKey’をfalseにし’conditions’に記述する
  • findするとき、’group’指定する

で、望みのSQLを吐き出してくれた。typeを省略すればLEFT JOINになるし、ユーザに条件を加えられるしで、なかなか使い勝手良いかも。modelに収めて隠ぺいすれば、controllerもスッキリ爽快気分になるに違いない(^o^)。

これと、group by句でposts.user_id指定したため、今日1日つぶした… orz。

以下、参考にしたサイトの記事です。ありがとうございました。

CakePHPのバージョンは、えーと、1.2.1_8004でした。