2009.04.18
HABTMのテーブルの検索とpaginate
CakePHPでハマったのでメモ。バージョンは1.2.0.7296 RC2。
HABTMのテーブルを検索する方法と、そのときのpagenateを正しく取得する方法です。
いわゆるBlogの記事に複数のタグを付けるとして、
以下のようなテーブルがあるとします。
————————————
posts
-id
-title
posts_tags
-post_id
-tag_id
tags
-id
-name
————————————
記事のタイトルとタグの名称の両方をLIKEで検索します。
コントローラのposts_controller.phpのsearchメソッドのなかで、paginateでデータを引っ張ってきます。
paginateのjoinsの指定で、posts_tagsとtagsをINNER JOINしてあげて、conditionsでLIKE検索の条件を渡してあげます。
$joins = array(
array(
'table' => 'posts_tags',
'alias' => 'PostsTag',
'type' => 'INNER',
'conditions' => 'PostsTag.post_id = Post.id'
),
array(
'table' => 'tags',
'alias' => 'Tag',
'type' => 'INNER',
'conditions' => 'Tag.id = PostsTag.tag_id'
)
);
$conditions = array(
'OR' => array(
'Post.title LIKE' => '%検索ワード%' ,
'Tag.name LIKE' => '%検索ワード%' ,
);
);
$this->paginate = array(
'joins' => $joins,
'conditions' => $conditions,
//複数のpostsが引っかかるので、idでまとめる
'group' => array('Post.id')
);
このとき、paginateでのページ送り機能が正しく動作しません。
これを回避するためモデルのpost.phpでpaginateCountメソッドを上書きします。取得するデータの総数を返してあげています。マニュアルにもさりげなく書いてあります。
function paginateCount($conditions, $recursive) { $where = “”; if($conditions){ $db =& ConnectionManager::getDataSource($this->useDbConfig); $where = $db->conditions($conditions); } $result = $this->query('SELECT Post.id FROM posts AS Post INNER JOIN posts_tags AS `PostsTag` ON (`PostsTag`.`post_id` = `Post`.`id`) INNER JOIN tags AS `Tag` ON (`Tag`.`id` = `PostsTag`.`tag_id`)’ .$where. ' GROUP BY `Post`.`id`’); return count($result); }
ちなみに、CakePHPはSQL文を書かなくてもある程度は自動で組み立てくれたりしてコードを書く量は減るけど、これをやるにはどうするの?という方法を調べるのに時間がかかります。自分の中でノウハウが蓄積されていけば開発スピードは上がりそうです。