14時の間食

CakePHPの記事を中心にWeb制作について

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にコードを追加

namebodycategoryという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] => %中古%
                        )
                )
        )
)

参考にさせていただいた記事

カテゴリー

最近の記事

Author

  • ささきち-このブログを書いてる人
    s3make@ささきち
    多摩川沿いに住むフリーのWebデザイナーです。近所のイオンによく行きます。