CakePHP2.X Search Pluginで複数キーワード×複数フィールド検索
CakePHP安西先生、『キーワード1 キーワード2 …』のようなスペース区切りのキーワードが与えられた時、例えば『CakePHP 本 中古』だった時、「CakePHP」「本」「中古」この3つのキーワード全てが含まれるレコードを抽出する検索がしたいです。
さらに、「CakePHP」「本」「中古」この3つのキーワードがテーブル内のname, body, categoryのいずれかのフィールドに入ってたらヒットさせたいです。 もちろんnameに「CakePHP」、bodyに「本」、categoryに「中古」など、それぞれのキーワードが1レコード内の各フィールドに散らばっていてもヒットさせたいです!
…という貪欲な検索です。
とりあえず動いたのでコードを載せておきます。もっとスッキリさせたいですが…。妙案がありましたらコメントでアドバイスをいただけると嬉しいです。
とその前に、これはCakeDCのSearch Plugin用のコードなので、Search Pluginを導入する必要があります。
Search Pluginの使用方法はトーハム紀行様のこちらの記事を参考にさせていただきました。本当にありがとうございました。
トーハム紀行 CakePHP2.0にSearch Pluginをいれてラクラク検索しよう【1/2】
作り方
Searchプラグインの設置
https://github.com/CakeDC/search
まず、GithubからSearchプラグインを入手します。Download ZIPボタンからダウンロードでOKです。
ZIPを解凍したら、解凍したフォルダ名を「Search」に変更しapp/Pluginの中に入れます。
あとはapp/Config/bootstrap.phpの中に
CakePlugin::load('Search');
と記述したら、Searchプラグインの設置と読み込みは完了です。
Viewに検索フォームを設置
フォームヘルパーでViewに検索フォームを作ります。
<?php
echo $this->Form->create('Model', array('action' => 'result', 'type' => 'post'));
echo $this->Form->input('search_word');
echo $this->Form->end('検索');
?>
ここでは検索結果を表示するアクションをresult、入力欄のname属性をsearch_wordとします。
Modelとなっているモデル名は適宜変更して下さい。(以降のコードでも同様です)
検索対象のModelにコードを追加
name・body・categoryという3つのフィールド内の文字データをくまなく検索するという想定です。 次のコードを検索対象のModel内に挿入します。
public $actsAs = array('Search.Searchable');
// 検索対象のフィルタ設定
public $filterArgs = array(
array('name' => 'search_word', 'type' => 'query', 'method' => 'multiWordSearch')
);
public function multiWordSearch($data = array()) {
$keyword = mb_convert_kana($data['search_word'], "s", "UTF-8");
$keywords = explode(' ', $keyword);
if (count($keywords) < 2) {
$conditions = array(
'OR' => array(
//検索対象のフィールド名、適宜変更や追加削除を行って下さい。
$this->alias.'.name LIKE' => '%' . $keyword . '%',
$this->alias.'.body LIKE' => '%' . $keyword . '%',
$this->alias.'.category LIKE' => '%' . $keyword . '%'
)
);
}else{
$conditions['AND'] = array();
foreach ($keywords as $count => $keyword) {
$condition = array(
'OR' => array(
//検索対象のフィールド名、適宜変更や追加削除を行って下さい。
$this->alias.'.name LIKE' => '%' . $keyword . '%',
$this->alias.'.body LIKE' => '%' . $keyword . '%',
$this->alias.'.category LIKE' => '%' . $keyword . '%'
)
);
array_push($conditions['AND'], $condition);
}
}
return $conditions;
}
ちょっと長いですね。
コード内にコメントでも書きましたが、検索対象のフィールド名は適宜変更や追加削除を行って下さい。
フィールドが1個だけでいい場合はもう少しシンプルになります。
その場合がこちら↓
public $actsAs = array('Search.Searchable');
// 検索対象のフィルタ設定
public $filterArgs = array(
array('name' => 'search_word', 'type' => 'query', 'method' => 'multiWordSearch')
);
public function multiWordSearch($data = array()) {
$keyword = mb_convert_kana($data['search_word'], "s", "UTF-8");
$keywords = explode(' ', $keyword);
if (count($keywords) < 2) {
$conditions = array(
//検索対象のフィールド名、適宜変更を行って下さい。
$this->alias.'.name LIKE' => '%' . $keyword . '%'
);
}else{
$conditions['AND'] = array();
foreach ($keywords as $count => $keyword) {
$condition = array(
//検索対象のフィールド名、適宜変更を行って下さい。
$this->alias.'.name LIKE' => '%' . $keyword . '%'
);
array_push($conditions['AND'], $condition);
}
}
return $conditions;
}
Controllerにコードを追加
続いてコントローラーです。
まずはコンポーネントを読み込んでください。
public $components = array('Search.Prg');
続いてアクションにコードを加えていきます。 先にも述べましたがresultというアクションで検索結果を表示する想定です。 ページネーションで検索結果を20件ずつ表示してみます。
public function result() {
// 検索条件設定
$this->Prg->commonProcess();
if (isset($this->passedArgs['search_word'])) {
//条件を生成
$conditions = $this->Model->parseCriteria($this->passedArgs);
$this->paginate = array(
'conditions' => $conditions,
'limit' => 20,
'order' => array(
'id' => 'desc'
)
);
$this->set('data', $this->paginate('Model'));
}
}
こんな感じです。このアクションのViewは割愛します。
ページネーションでうまくデータが取得できたら後はそれをViewで好きなように表示してやるだけです。
生成される$conditionsの連想配列
ちなみにこのコードを使い、最初の例に挙げた『CakePHP 本 中古』を検索すると$conditionsはこんな感じになります。
Array
(
[AND] => Array
(
[0] => Array
(
[OR] => Array
(
[Model.name LIKE] => %CakePHP%
[Model.body LIKE] => %CakePHP%
[Model.category LIKE] => %CakePHP%
)
)
[1] => Array
(
[OR] => Array
(
[Model.name LIKE] => %本%
[Model.body LIKE] => %本%
[Model.category LIKE] => %本%
)
)
[2] => Array
(
[OR] => Array
(
[Model.name LIKE] => %中古%
[Model.body LIKE] => %中古%
[Model.category LIKE] => %中古%
)
)
)
)