WordPress で カテゴリーをチェックボックスで絞る
概要
ECサイトなどではよくある実装ですが、一覧ページで絞り込んで表示したい場合があると思います。
WordPress の場合、カテゴリーで絞り込みたいことがよくあると思います。
前提
カスタム投稿「blog」、カスタムタクソノミー「blog_cat」を発行し、カスタムタクソノミーを絞り込んで表示させます。OR検索ではなくAND検索です。
HTML
まずチェックボックスを発行します。
/**
* ブログカテゴリー:チェックボックスを出力
*
* @return string
**/
function blogTaxCheckbox(): string
{
$output = '';
$args = [
'hide_empty' => false,
];
$terms = get_terms('blog_cat', $args);
if (is_wp_error($terms) || !$terms) return '';
// ここでGETパラメータを取得し、サニタイズ
$selected_cats = isset($_GET['blog_search']) ? $_GET['blog_search'] : null;
$selected_cats = is_array($selected_cats) ? array_map('sanitize_text_field', $selected_cats) : sanitize_text_field($selected_cats);
foreach ($terms as $term) {
// チェックボックスの値と一致するかチェック
$checked = is_array($selected_cats) && in_array($term->term_id, $selected_cats) ? 'checked' : '';
$output .= '
<span class="mwform-checkbox-field horizontal-item">
<label>
<input type="checkbox" name="blog_search[]" value="' . $term->term_id . '" ' . $checked . '>
<span class="mwform-checkbox-field-text">' . esc_html($term->name) . '</span>
</label>
</span>
';
}
return $output;
}
<form method="get" action="<?php echo esc_url(home_url('/')); ?>">
<div class="select-area02">
<div class="checkbox-list contact-form">
<?php echo blogTaxCheckbox(); ?>
</div>
<div class="btn-search">
<input type="submit" value="このカテゴリーで検索">
</div>
</div>
<!-- Hidden input for post type -->
<input type="hidden" name="post_type" value="blog" />
</form>
こんな感じのチェックボックスが発行されます。
pre_get_posts
GETで渡された値に対して、pre_get_posts でクエリを発行します
function change_posts_per_page($query)
{
/* 管理画面,メインクエリに干渉しないために必須 */
if (is_admin() || !$query->is_main_query()) {
return;
}
if (isset($_GET['post_type']) && $_GET['post_type'] === 'blog') {
$query->set('post_type', 'blog');
$tax_query = blogTaxSearchQuery();
if ($tax_query) {
$query->set('tax_query', $tax_query);
}
return $query;
}
}
add_action('pre_get_posts', 'change_posts_per_page');
/**
* ブログカテゴリー:チェックボックスによる絞り込み検索
* GETで渡ってきたidをpre_get_postsに渡す
*
**/
function blogTaxSearchQuery()
{
$categories = isset($_GET['blog_search']) ? $_GET['blog_search'] : null;
if (!$categories) return;
$categories = is_array($categories) ? array_map('sanitize_text_field', $categories) : sanitize_text_field($categories);
if (!is_array($categories)) return;
$tax_query = [
[
'taxonomy' => 'blog_cat',
'field' => 'term_id',
'terms' => $categories,
'operator' => 'AND',
],
];
return $tax_query;
}
ハマりどころ
チェックボックスの name をタクソノミー名と同じにしていたところ、大いにハマりました。
pre_get_posts で設定した tax_query の他に何故かもうひとつ tax_query が発行され、そちらが優先されてしまい、期待する投稿が表示されませんでした。
WordPress では様々なルールによるルーティングが設定されていますが、URLによるルーティングももちろん行われます。
そのため、チェックボックス name を blog_cat にした場合、
?blog_cat%5B%5D=7&blog_cat%5B%5D=1&post_type=blog
のようなクエリ列になり、カスタムタクソノミーblog_cat のルーティングが走ってしまいます。
pre_get_posts で設定した tax_query の後に走ってしまうため、意図したルーティングにならない現象で大いに悩みました。
解決はシンプルで、タクソノミー名と同じ name を使わないだけです。
ちなみにコードは ChatGPT に質問しながら作りました。
先ほどのハマりどころで落ち着いて考えれば解決出来たのですが、有能な ChatGPT ならそのまま解決出来るかもと投げた結果、沼にハマった次第です
Discussion