【WordPress】絞り込み検索機能を実装する方法|セレクトボックス・チェックボックス・pre_get_posts

WordPress

WordPressの標準検索ではカテゴリやタグでの絞り込みができません。検索フォームにフィールドを足し、pre_get_posts で検索結果をフィルタリングすれば、カテゴリ絞り込み付きの検索を実装できます。この記事ではセレクトボックスチェックボックスの2パターンを、サニタイズなどのセキュリティ注意点込みで解説します。

この記事でわかること

  • セレクトボックスでカテゴリを1つ選んで絞り込む
  • チェックボックスでカテゴリを複数選んで絞り込む
  • pre_get_posts でのフィルタリングと is_search() ガード
  • $_GETパラメータのサニタイズabsint / intval
$_GET はそのままクエリに渡さない
検索フォームから送られる $_GET の値はユーザーが自由に書き換えられるため、無検証でクエリに渡すのは危険です。カテゴリIDは absint()(単一)や array_map('intval', ...)(配列)で整数に変換してから使います。また、フィルタは is_search()検索結果ページに限定し、トップページやアーカイブにまで適用されないようにします。
スポンサーリンク

セレクトボックスでカテゴリを選んで絞り込む

まず検索フォームに、キーワード入力欄とカテゴリのセレクトボックスを置きます。wp_dropdown_categories()namecat にすると、選んだカテゴリIDが cat パラメータで送信されます。

PHP:検索フォーム(セレクトボックス)
<form role="search" method="get" id="searchform" action="<?php echo esc_url(home_url('/')); ?>">
  <label for="s">キーワード検索:</label>
  <input type="text" value="<?php echo get_search_query(); ?>" name="s" id="s" />

  <label for="category">カテゴリ:</label>
  <?php wp_dropdown_categories(array(
    'show_option_all' => 'すべてのカテゴリ',
    'orderby'         => 'name',
    'name'            => 'cat', // 送信されるパラメータ名
    'id'              => 'category',
  )); ?>

  <input type="submit" id="searchsubmit" value="検索" />
</form>

次に functions.php で、送られた cat を使って検索結果を絞り込みます。absint() で整数化し、is_search() で検索時だけ適用します。

functions.php:セレクトで絞り込む(サニタイズ済み)
function my_search_filter($query) {
  // 管理画面・メイン以外・検索以外は対象外
  if (is_admin() || !$query->is_main_query() || !$query->is_search()) {
    return;
  }
  // $_GET は必ずサニタイズ(カテゴリIDは整数)
  $cat = isset($_GET['cat']) ? absint($_GET['cat']) : 0;
  if ($cat) {
    $query->set('cat', $cat);
  }
}
add_action('pre_get_posts', 'my_search_filter');

セレクトボックスでの絞り込みだけならカテゴリーやタグのセレクトボックスを使って記事を絞り込む方法でも詳しく扱っています。

チェックボックスで複数カテゴリを選んで絞り込む

複数カテゴリで絞り込むには、name="category[]"配列として送信します。チェック状態の判定で $_GET['category'] を参照する際は、未定義(初回表示)でも警告が出ないよう isset でガードします。

PHP:検索フォーム(チェックボックス)
<form role="search" method="get" action="<?php echo esc_url(home_url('/')); ?>">
  <label for="s">キーワード検索:</label>
  <input type="text" value="<?php echo get_search_query(); ?>" name="s" id="s" />

  <p>カテゴリ:</p>
  <?php
  // 未定義でも警告が出ないよう isset でガードして配列化
  $selected = isset($_GET['category']) ? (array) $_GET['category'] : array();
  foreach (get_categories() as $category) : ?>
    <label>
      <input type="checkbox" name="category[]"
             value="<?php echo esc_attr($category->term_id); ?>"
             <?php checked(in_array((string) $category->term_id, $selected, true)); ?> />
      <?php echo esc_html($category->name); ?>
    </label>
  <?php endforeach; ?>

  <input type="submit" value="検索" />
</form>

フィルタ側では、配列のIDを array_map('intval', ...) で整数化し、category__in に渡します。

functions.php:チェックボックスで絞り込む(サニタイズ済み)
function my_search_filter($query) {
  if (is_admin() || !$query->is_main_query() || !$query->is_search()) {
    return;
  }
  // 配列の各IDを整数化
  $cats = isset($_GET['category']) ? array_map('intval', (array) $_GET['category']) : array();
  if ($cats) {
    $query->set('category__in', $cats);
  }
}
add_action('pre_get_posts', 'my_search_filter');
関数名の重複に注意
上の2つの my_search_filterどちらか一方だけ使ってください(両方定義すると関数名が衝突します)。リアルタイム(ページ遷移なし)で絞り込みたい場合はAjaxでチェックボックスを使ったリアルタイム絞り込み検索が向いています。

タグ・カスタムフィールドへの応用と高速化

同じ要領で、タグ(tag__in)やカスタムフィールド(meta_query)、カスタムタクソノミー(tax_query)でも絞り込めます。複雑な条件(AND/OR/NOT)の組み立てはWP_Queryで複雑な条件検索を実装する方法meta_query/tax_query のパフォーマンス最適化は検索結果を高速化するカスタムクエリ最適化術pre_get_posts を含むフックの仕組みはフィルターフックとは?を参照してください。

よくある質問(FAQ)

Q複数条件(カテゴリ・タグ・カスタムフィールド)で絞り込むには?
AGETパラメータを WP_Querytax_querymeta_query に渡します。例:new WP_Query(["tax_query" => [["taxonomy"=>"category","field"=>"term_id","terms"=>$cats]]]);。キーワードは sanitize_text_field()、IDは absint() / intval() で必ずサニタイズしてください。
Qフォーム送信後に検索結果を表示するには?
Amethod="get" のフォームで送信し、検索結果ページ(is_search())のメインループで通常どおり表示します。pre_get_posts でGETパラメータを読み取り、クエリに反映させるのがポイントです。ページ遷移なしにしたい場合は admin-ajax.php や REST API を使います。
Q絞り込み検索プラグインは何が使いやすいですか?
A高機能なのは FacetWP・JetSmartFilters(有料)です。無料では Search & Filter・Relevanssi などがあります。シンプルな絞り込みなら、この記事のような自前のカスタムクエリが軽量で柔軟です。

まとめ

  • セレクトwp_dropdown_categories(["name"=>"cat"])$query->set("cat", absint($_GET["cat"]))
  • チェックボックスname="category[]"category__inarray_map("intval", ...)
  • 共通is_search() で検索時に限定し、$_GET は必ずサニタイズ
  • タグ・カスタムフィールドへは tag__in / meta_query / tax_query で応用

サニタイズと is_search() ガードを守れば、軽量で安全な絞り込み検索を自作できます。