qudanblog

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メソッドを上書きします。取得するデータの総数を返してあげています。マニュアルにもさりげなく書いてあります。

→ 4.9.4 カスタムしたクエリによるページ付け


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文を書かなくてもある程度は自動で組み立てくれたりしてコードを書く量は減るけど、これをやるにはどうするの?という方法を調べるのに時間がかかります。自分の中でノウハウが蓄積されていけば開発スピードは上がりそうです。


Accept as true only
what is indubitable

明証的に真であると認めたもの以外、決して受け入れない事。

Divide every question
into manageable parts

考える問題を出来るだけ小さい部分にわける事。

Begin with the simplest
issues
and ascend to
the
more complex

最も単純なものから始めて複雑なものに達する事。

Review frequently
enough to retain the
whole argument at once

何も見落とさなかったか、全てを見直す事。

René Descartes 1637