2019-08-28

Craft CMS で POST でフィールドごとの検索(絞り込み)を実装してみる #craftcms


キーワード検索はブログにつけたけど、絞り込みというかフィールドごとの検索とかは試してないなということで試してみた。

テンプレートに検索条件と同じような絞り込みをかけばいけるのはなんとなくわかってたけど、検索投げたら結果が返ってくる、みたいなのをやれないか?ということで。

POSTでやってみたけど、 @tinybeans @BUN の教えに従ってパラメータベースでやったほうが良さそうというのがとりあえずの結論。

検索というか絞り込みも CMS 側でやるか Vue  とかでやるってのもあるのでそこはそこで考えるところかな、と。


getBodyParam を使って受け取る

検索フォームの方から投げた値を受け取るときには getBodyParam() が使える。

craft\web\Request | Craft 3 Class Reference
https://docs.craftcms.com/api/...

これで渡ってきている値を取ることができる。

この辺やり取りするときはフォーム側に以下も必要。

{{ csrfInput() }}

値は連想配列で渡すと受け取りやすそう

@tinybeans にアドバイスもらって name="fields[foo]" みたいにかけば取り出しやすいということで試してみた。

フォームの選択肢は

<p class="c-keyword-search__control-title">カテゴリ</p>
<select class="c-keyword-search__control-select" name="fields[sample_publisher_category]">
<option value="">すべて</option>
{% set searchCategories = craft.entries.section('sample_publisher_category').limit(null) %}
{% for entry in searchCategories.all() %}
<option value="{{ entry.slug }}">{{ entry.title }}</option>
{% endfor %}
</select>

こんな感じでエントリで持ってるものとかはそれをループさせた上で、name にはあとで取り出しやすいようにフィールドのハンドル sample_publisher_category をベースにしてみた。

フィールドの取り出し方はこの前書いた感じで。

こんな感じで渡しておくと受け取る側で(今回は同じURLでやり取りしてますが)

{% set searchParams = craft.app.request.getBodyParam('fields') %}
{# リクエスト:{{ dump(searchParams) }} #}

こんな感じで取り出せる。

連想配列は key,value でそのままとれる

これらが渡ってきたらこの中身は

{% for key, value in searchParams %}
{{key}}:{{value}}
{% endfor %}

のループで取り出せる。

searchParamssearchParam とかでループして {{searchParam.key}}, {{searchParam.value}} とかやるのかと最初思った。

検索表示させるためにsearchQueryに何を渡すか

検索結果部分の表示としては

{% set entries = craft.entries.section('sample_publisher_book')
    .search(searchQuery)
    .orderBy(sortKey)
    .all() %}

という感じでセットしたものを entries のループで回せばいいので searchQuery に何をどう渡すか?というところ。

アドバイスもらってこんな感じで searchQuery に投げてあげればいい感じに検索は動いた。

{% set searchQuery = searchQuery ? searchQuery ~ ' OR ' ~ key ~ ':' ~ value : key ~ ':' ~ value %}

選択肢とかをかえてみて動いてるのかどうかわからんなー、と最初思ったりしたけど、単にサンプルデータが少なくてヒットしてなかっただけだった。

別セクションのエントリを選択してるフィールド(リレーション)とかが面倒かな?と思ったけどそんな事なく、わたせば問題なく動いた。

逆にキーワード検索のほうが q:キーワード では動かなくて q=キーワード で動いた。

最終的にはキーワードは value だけ渡せば良さそうだったので以下のように変更した。

{% set searchQuery = searchQuery ? searchQuery ~ ' OR ' ~ key ~ ':' ~ value : value %}

たまたまキーワードが最初だったのでこれでまわるけど、keyの有無によって渡し方を変えたほうがいい、というアドバイスももらった。
確かにそうだな。。。

検索結果を表示するソート順を指定する

ソート順も .orderBy(sortKey) のような感じで渡せるので 

<p class="c-keyword-search__control-title">ソート</p>
<select class="c-keyword-search__control-select" name="fields[sortKey]">
<option value=""></option>
<option value="desc">発売日が最近のものから</option>
<option value="asc">発売日が古いものから</option>
</select>

で一緒に渡してきて、検索パラメータからは外して別に orderBy に渡すことにした。

{% set sortKey = 'bookPublishingDate ' ~ value %}

という感じで、販売日を設定していたフィールドのハンドルに昇順降順(asc/desc)のを半角スペースでつなぐ感じでいけた。

この orderBy に何が渡せるのかドキュメント見ても特に書いてないのだけど @BUN に教えてもらったところでは複数設定できたり、数値型のフィールドとか色々使えるっぽい。

キーワード検索のsyntax

キーワード検索については @tinybeans が書いてる感じで

Craft CMS のエントリー一覧の検索を単語の途中でも一致するようにする | Craft CMS | かたつむりくんのWWW
https://tinybeans.net/blog/201...
'defaultSearchTermOptions' => array(
 'subLeft' => true,
 'subRight' => true,
),

の設定をしておけばアスタリスクとかなくても検索ができる。

その辺の検索条件の話は以下に書かれてある。

Searching | Craft 3 Documentation
https://docs.craftcms.com/v3/s...

とりあえずこんな感じで検索を書いてみたけど、アドバイスもらってたとおりパラメータとりだしながらテンプレートを組み立てていったほうが良さそうというのが最初に書いたけど結論。

パラメータの取得については @tinybeans がエントリまとめてくれました。

Craft CMS のテンプレートでパラメータを取得する方法のまとめ | Craft CMS | かたつむりくんのWWW
https://tinybeans.net/blog/201...

なんとなく自分で試してみないと納得がいかないというところもあったのだけど、試してみて納得がいったというか、先人の言うことは素直に従っておきましょうという感じ。

自力でやろうと頑張ってみたところですぐ挫折したけど、聞ける人がいると本当にありがたいなー、と思った。
感謝感謝。