カスタムフィールドの絞り込み検索をプラグインなしで実装する

WordPressにおける絞り込み検索の実装では「Advanced Custom Fields(ACF)× Search & Filter Pro」の組み合わせが個人的におすすめですが、Pro版は年間$20かかるので、独自にコードを書いて実装することがあります。普段からKOTORIさんのブログを参考にするのですが、毎回よく分からなくなるので備忘録として残しておきます。

ポイント

  • Advanced Custom Field の出力は特殊なので Custom Field Template(CFT)を使うべし
  • カテゴリ検索以外は meta_query に追加するべし
  • 検索フォーム側の value="" の言葉で検索できる
    • ただしlabelや値(priceやtrueなど)ではうまく検索できず
  • 検索フォーム側はクエリを連結して投げるだけで、絞り込み処理は検索結果一覧(search.phpやindex.php)に書く
  • wp_reset_query(); を忘れないように

実装する前に確認しておいた方がいいこと

コードを書く前に以下の点を確認しておくのがおすすめです。

  • どの場所に(サイドバー、トップ、検索結果、固定ページ、投稿記事)
  • どんなタイプの検索項目を
  • それぞれ何の選択肢で
  • 範囲指定の場合、単位(刻み幅)はいくつで
  • 何項目実装するのか
  • モバイルでの表示位置は?
  • ヘッダーなどにある標準の検索フォームは非表示にするか
  • AMPページでも動かすのか
  • そもそもテーマに sidebar.php がちゃんと存在しているか

また独自実装だと、原則最初に決めた項目・順番・選択肢は後から(管理画面からは)変更不可ですが、クライアント側で初めに項目や選択肢が決まってない・ぼんやりしていることが多く、要件を詰めたりと実装以外の工数がかかることが多いです。このあたりをしっかり踏まえて見積もりをしましょう。(そう考えると素直にSearch & Filter Proをおすすめするのが、双方にとって負担が楽で一番いいかなぁ..)

コード

キーワード検索、チェックボックス、ドロップダウン、価格帯で絞る場合のサンプルコードです。KOTORIブログさんのコードほぼそのままですが、if文の追加や改行などを私なりにアレンジしてます。

注意点

なぜACFでなくCFTを使うのか

ACFで作成したカスタムフィールドはDBへの登録が特殊なようでうまく絞り込みできません(何をkeyにしていいか分からなかった)。その特殊性ゆえに get_field('price'); のように書くだけで、カスタムフィールドの値を取得できたりするのですが、Search & Filter Proと組み合わせない限り、CFTを使用した方が確実です。

ただし今回は検索フォーム側に選択肢を直に書いていますが、ACFの独自関数などを使えば、ACFで増やした選択肢が検索フォームにも自動反映されるとかもできそうではありました。

カスタムフィールドの検索は基本 meta_query に追加すべし

カテゴリ検索は tax_query を使いますが、カスタムフィールド はチェックボックスも範囲選択もドロップダウン(<select></select>)も、すべて meta_query に追加でいけます。

標準のキーワード検索フォームの機能を損なわないように注意

検索フォームのキーワード検索を利用すれば、URLに hoge.com/?s="りんご"&low_price="300円" のようにクエリがつきますが、WordPress標準の検索フォームで検索すると /?s="りんご" しかクエリがつきません。なので、検索結果一覧側である search.php や index.php で、仮に(標準の検索フォームを使われて)クエリにpriceなどが付いてない時もきちんとキーワード検索できるようにしないといけません。

$low = $_GET['low'];
$high = $_GET['high'];

// どちらか価格帯のクエリが存在している時だけ metaquerysp[] に追加する
if($low || $high) {
  if(!($low == 0 && $high == 9999999)) {
    $metaquerysp[] = array(
      'key'=>'my_price',
      'value'=>array( $low, $high ),
      'compare'=>'BETWEEN',
      'type'=>'NUMERIC',
    );
  }
}

また価格帯で絞る時など、初期値が「下限なし」〜「上限なし」のようになっていると、その下限なし/上限なしのvalueがクエリとして渡されてしまうので、その場合も metaquerysp[] に値が追加されないようにしないといけません。

$low = $_GET['low'];
$high = $_GET['high'];

if($low || $high) {
  // ただし値が初期値の場合はスルー
  if(!($low == 0 && $high == 9999999)) { 
    $metaquerysp[] = array(
      'key'=>'my_price',
      'value'=>array( $low, $high ),
      'compare'=>'BETWEEN',
      'type'=>'NUMERIC',
    );
  }
}