friendsofcake/search を使っての検索は非常に楽なんですが、いろいろわからない点が多いんですよね。
カラムを連結して検索する要件があたのですが、手間取ったので記事にしました。
CakePHP2の場合
CakePHP2の場合は非常に簡単です。
virtualfieldsという機能があって、これを使えばテーブルにカラムがあるのと同じように扱えばOKです。
CakePHP3以降は?
残念なことにCakePHP3以降ではvirtualfieldsが亡くなってしまいました。
代わりに仮想プロパティーというものになりました。
ただし、これはオブジェクトに対してしか使えず、ここで定義したプロパティーは検索には使えませんので、今回の要件には合いません。
ということで、SQLのconcat関数で結合してあげてから検索するわけですが、これがまた曲者です。
$query = $this->Users->find() ->select(['full_name'=>$query->func()->concat(['last_name'=>'identifier','first_name'=>'identifier'])],false);
これで、データを取得するとfull_nameというプロパティーができてて、意図通りに名前がつながった形で取得できるのですが、これを検索キーに使等とすると、どうやってもできない。
勝手にカラム名が変換されてしまって意図したとおりのSQL文が生成できないんです。
cakebookにはなさそうなんですが、Where句の中でつなげてあげてやるといいみたいです。
$query = $this->Users->find(); $query = $query->where(function ($exp, $query) use ($keyword) { $concat = $query->func()->concat([ 'last_name' => 'identifier', 'first_name' => 'identifier' ]); return $exp->like($concat, $keyword); });
というわけで、これをfriendsofcake/searchで実現するにはどうしたらいいか?
こんな感じで実装しました。
//Tableクラスの中のinitialize()メソッドの中で… $this->searchManager() ->add('keywords', 'Search.Callback',[ 'callback' => function (Query $query, array $args, \Search\Model\Filter\Base $filter) { $query->where([ //return で返す? function ($exp,$query) use ($args){ $concat = $this->find()->func()->concat([ 'last_name'=>'identifier', 'first_name'=>'identifier', 'last_name_kana'=>'identifier', 'first_name_kana'=>'identifier', 'email'=>'identifier', ]); //複数キーワードのAND検索に対応 $keywords = explode(" ",mb_convert_kana($args['keywords'],'s')); foreach($keywords as $keyword) $query->like($concat,"%{$keyword}%"); //return で返す? } ]); } ]);
Search.Like を使えば複数キーワードを指定できるのですがconcatを使う場合は無理みたいなので、自前で分解してlikeメソッドに入れています。
あと、ネットを検索すると、無名関数の戻り値としてQueryオブジェクトを返すように書いてあるのですが、そのようにすると型が違うというようなエラーになってしまいます。おそらくPHPのバージョンで型が厳密になったためと思います。
追記
concat()で追加したフィールドがnull可になっていて値にNULLが入っていると、そのレコード自体が検索対象にならなくなってしまいます。
この場合は、以下のようにIFNULL()を追加してあげる必要があるようです。
'IFNULL(last_name,"")'=>'identifier',