WordPressの標準検索ではカテゴリやタグでの絞り込みができません。検索フォームにフィールドを足し、pre_get_posts で検索結果をフィルタリングすれば、カテゴリ絞り込み付きの検索を実装できます。この記事ではセレクトボックスとチェックボックスの2パターンを、サニタイズなどのセキュリティ注意点込みで解説します。
- セレクトボックスでカテゴリを1つ選んで絞り込む
- チェックボックスでカテゴリを複数選んで絞り込む
pre_get_postsでのフィルタリングとis_search()ガード- $_GETパラメータのサニタイズ(
absint/intval)
検索フォームから送られる
$_GET の値はユーザーが自由に書き換えられるため、無検証でクエリに渡すのは危険です。カテゴリIDは absint()(単一)や array_map('intval', ...)(配列)で整数に変換してから使います。また、フィルタは is_search() で検索結果ページに限定し、トップページやアーカイブにまで適用されないようにします。セレクトボックスでカテゴリを選んで絞り込む
まず検索フォームに、キーワード入力欄とカテゴリのセレクトボックスを置きます。wp_dropdown_categories() の name を cat にすると、選んだカテゴリIDが cat パラメータで送信されます。
<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() で検索時だけ適用します。
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 でガードします。
<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 に渡します。
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)
WP_Query の tax_query や meta_query に渡します。例:new WP_Query(["tax_query" => [["taxonomy"=>"category","field"=>"term_id","terms"=>$cats]]]);。キーワードは sanitize_text_field()、IDは absint() / intval() で必ずサニタイズしてください。method="get" のフォームで送信し、検索結果ページ(is_search())のメインループで通常どおり表示します。pre_get_posts でGETパラメータを読み取り、クエリに反映させるのがポイントです。ページ遷移なしにしたい場合は admin-ajax.php や REST API を使います。まとめ
- セレクト:
wp_dropdown_categories(["name"=>"cat"])+$query->set("cat", absint($_GET["cat"])) - チェックボックス:
name="category[]"+category__in(array_map("intval", ...)) - 共通:
is_search()で検索時に限定し、$_GETは必ずサニタイズ - タグ・カスタムフィールドへは
tag__in/meta_query/tax_queryで応用
サニタイズと is_search() ガードを守れば、軽量で安全な絞り込み検索を自作できます。

