CakePHP2 Search PluginのpresetVarsとfilterArgsについて調べてみた

こんにちは @kaihiro です。

CakePHP歴2ヶ月の初心者ですがよろしくお願いします。
これまでは主にJavaでの開発をメインに行っていました。

会社で隣に座っている @k1LoW さんからCakePHPのAdvent Calendar書かないの？って脅されたので頑張ります。

## CakeDC Search Plugin

今回、自分で CakeDC の Search Plugin を使用した検索機能を実装してみて、 __presetVars__ と __filterArgs__（特にpresetVars）が何を設定しているものなのか最初よく分からなかったので調べたことを書きたいと思います。

presetVars と filterArgs にポイントを絞ってるので、Search Pluginの使い方については他のサイトを参考にしてください。

##filterArgs

まずは filterArgs から。

Modelに定義する __filterArgs__ は、検索条件を設定するプロパティです。
検索用のパラメータが渡されたときにどのような検索を行うかを定義します。

```定義例
class Post extends AppModel {
    public $actsAs = array('Search.Searchable');
    public $belongsTo = array('User');

    public $filterArgs = array(
        'user_id' => array(
            'type' => 'value'
        ),
        'keyword' => array(
            'type' => 'like',
            'field' => array(
                'User.username',
                'Post.body'
            )
        ),
    );
```

filterArgs は検索条件を設定する項目なので、何をやっているかはイメージしやすいと思います。
__type__ にはlike, ilike, value, expression, subquery, queryを使用して、検索条件を指定することができます。

##presetVars

次に presetVars です。

Controller に指定する __presetVars__ は、検索パラメータを管理するための設定値です。
検索条件をURLのパラメータに保持するときのフォーマットや実際にSQLに渡す検索条件を構成する処理、検索後に検索フォームに検索条件を設定する処理に影響します。

せっかく filterArgs の設定をおこなっていても、presetVars が設定されていないと検索条件として使用してくれません。

Controller の例です。

```定義例
class PostsController extends AppController {

    public $components = array('Paginator','Search.Prg');

    public $presetVars = array(
        'user_id' => array(
            'type' => 'value'
        ),
        'keyword' => array(
            'type' => 'value'
        )
    );

    public function index() {
        $this->Prg->commonProcess();

		$this->Paginator->settings['conditions'] = 
            $this->Post->parseCriteria($this->Prg->parsedParams());

        $this->set('posts', $this->Paginator->paginate());
    }
```

###検索処理の流れ

上記のindexの処理を簡単に説明します。

__$this->Prg->commonProcess()__は検索条件データのハンドリングを行います。
検索条件がPOSTされると、そのデータを __presetVars__ の定義にしたがって、検索条件を含んだURLを構成し、現在のactionにリダイレクトします。
リダイレクトにより再度、$this->Prg->commonProcess()がGETで呼ばれると、今度はURLに含まれる検索条件を解析して、その結果が __parsedParams__ に設定されます。

あとはその parsedParams を __$this->Post->parseCriteria()__ に渡すことで、Model の filterArgs に定義した内容にしたがってwhere条件が構成され、検索が行われます。

###presetVarsのtype

presetVars の type には、value, checkbox, lookupを設定できます。
単一の入力項目に対しては __value__ を使用します。
複数選択可能なチェックボックスの場合は __checkbox__ を指定できます。

複数選択のチェックボックス入力の場合に type を __value__ とすると、URLのパラメータが配列形式となります。

```
?user_id[0]=1&user_id[1]=4&keyword=hoge
```

type が __checkbox__ の場合は、`|`(パイプ)で区切られます。

```
?user_id=1|4&keyword=hoge
```

例えば、上記の user_id の type が checkbox の場合に、あえて配列形式でURLパラメータを渡してみると、当然ながら検索条件が想定通りに使用されず、画面の検索フォームにも反映されません。

presetVarsには type の他にも __encode__ や __empty__ などのパラメータがあります。

##presetVarsとfilterArgsの関連

検索条件として presetVars に定義する項目と、filterArgs に定義する項目は基本的に同じになるかと思います。
そこで以下のように記述することで __presetVars の定義を省略することができます。__


```
    public $presetVars = true;
```


この場合、presetVars は Controller にてメインで使用する Model の filterArgs の値を引き継ぎます。
type の値は presetVars と filterArgs で異なるため、presetVars の type として有効でないものは PrgComponentにて __value__ に置き換えています。
ですから、presetVars の type に checkbox を指定したい場合は、presetVars を省略せずに明示的に指定する必要があります。


また公式ドキュメントには記載されていませんが、presetVars省略時、filterArgs に __presetType__ を定義しておくと、その値を presetVars の type として使用してくれるようです。

```定義例
    public $filterArgs = array(
        'user_id' => array(
            'type' => 'query',
            'method' => 'searchByUsers',
            'presetType' => 'checkbox' // presetVarsのtypeを指定
        ),
        'keyword' => array(
            'type' => 'like',
            'field' => array(
                'User.username',
                'Post.body'
            )
        ),
    );
```

Modelにまとめて定義してしまいたい方はこちらの方法もありかもしれません。


##CakePHPを使ってみての感想

これまでJavaで開発してきた自分からすると、CakePHPでの開発はスピード感がずいぶん違うなぁと感じました。 
Search Plugin のような便利なプラグインがたくさんあるのも魅力ですね。

CakePHP3についてもこれから触っていきたいと思います！