CakePHPのsearch pluginのサンプルコードと記事をちょっと修正。Containableは必要でした
CakeDCのsearch pluginの記事が少ないので1個置いときますね。CakePHP Advent Calendar 2010 8日目 - kanonjiの日記のコードをちょっとだけ直しました。タグ検索などを実装するHABTMの検索で、プラグインのReadme.mdにはあったContainableビヘイビアを使わなかったんですが、やっぱり必要でした。無くても動くけど、余計なJOINが発生します。
修正点
$ git diff --no-prefix HEAD~ HEAD models/entry.php diff --git models/entry.php models/entry.php index 70c7aef..1b54b43 100644 --- models/entry.php +++ models/entry.php @@ -81,19 +81,23 @@ class Entry extends AppModel { } public function searchByGroupps($data = array()){ + $this->User->GrouppsUser->Behaviors->attach('Containable', array('autoFields' => false)); $this->User->GrouppsUser->Behaviors->attach('Search.Searchable'); $query = $this->User->GrouppsUser->getQuery('all', array( 'conditions' => array('groupp_id' => explode('|', $data['groupp_id'])), 'fields' => array('user_id'), + 'contain' => $this->User->Groupp->alias, )); return $query; } public function searchByTags($data = array()){ + $this->EntriesTag->Behaviors->attach('Containable', array('autoFields' => false)); $this->EntriesTag->Behaviors->attach('Search.Searchable'); $query = $this->EntriesTag->getQuery('all', array( 'conditions' => array('tag_id' => explode('|', $data['tag_id'])), 'fields' => array('entry_id'), + 'contain' => $this->Tag->alias, )); return $query; }https://github.com/kanonji/CakePHP-Search-plugin-sample/commit/38ee2a20dd374bd7bb72c8a3e814fed8c0451590
HABTMの検索にはIN演算子を使ったサブクエリーが使われます。[https://github.com/CakeDC/search/blob/master/models/behaviors/searchable.php#L106:title=getQuery()]
でサブクエリーのSQLが生成されるので、検索全体でのContainableとは別に、Containableのアタッチとcontain
の設定が必要みたいです。
array('autoFields' => false)について
なお、array('autoFields' => false)
はauto-add needed fields to fetch requested bindings
というものらしく、よく分かりませんがtrue
だと何か必要とされるフィールドを勝手に追加するようになるらしい。デフォルトがtrue
なので明示的にfalse
にしているようです。実際にtrue
のままだと、'fields' => array('user_id')
と指定しているのにGroupp.id
が追加されました。IN演算子に渡すので2つ以上のフィールドを取得するとSQLエラー*1となってしまいます。
SQLでの違い
--- before.sql 2011-02-15 13:15:14.000000000 +0900 +++ after.sql 2011-02-15 13:15:26.000000000 +0900 @@ -27,9 +27,6 @@ FROM groupps_users AS GrouppsUser LEFT JOIN - users AS USER ON - (`GrouppsUser`.`user_id` = `User`.`id`) - LEFT JOIN groupps AS Groupp ON (`GrouppsUser`.`groupp_id` = `Groupp`.`id`) WHERE
SQLで言うとこんな違いです。JOINする必要のないusersテーブルをJOINしちゃってました。これは、サンプルアプリで言うところのグループでの検索ですが、タグ検索のほうを動かせばentriesテーブルを余計にJOINしちゃっていました。
ところで
プラグインのReadme.mdだと、このサブクエリーを生成するメソッドってfindByTags($data = array())
とかになってるんですよね。http://book.cakephp.org/ja/view/1026/findByと被ってる上に、メソッド内で$data['tags']
と決め打ちだからコントローラーから呼べる作りじゃないし、微妙だなぁと思ってsearchByTags()
にしてみました。でも、サブクエリーのSQLが貰えるだけのメソッドだから、もっと別の名前の方が良かったなと思ったり。
環境
Mac | Mac OS X 10.5.8(Leopard) |
MAMP | 1.7.2 |
CakePHP | 1.3.6 |
php | 5.2.6 |
CakeDC Search plugin | updating readme · CakeDC/search@668eb68 · GitHub |
*1:#1241 - Operand should contain 1 column(s)