WordPressに複合検索機能を付ける方法

ブログの検索機能って使う?

私は使ったことないです……。

求める情報がなかった場合、そこのブログ内でいちいち検索してまで探さない気がします。だってもっとふさわしい記事があるならGoogleが拾っているはずだし、そうあるべきでは??
すっかりGoogleに飼いならされた傲慢かつ怠惰なユーザーという感じがしますね。

というわけで、実装するなら自分が使いやすい検索機能を付けたいと思いました。
そんな難しくないでしょ、と思ってたんですがWordPressの仕様を把握してなかったので時間がかかりました。以下、「こう実装した」という記録です。もっといい方法があるかもしれません。

検索エンジンみたいなAND検索はできない

WordPressにはキーワードから記事を検索する機能があるんですが、そもそもちょっと使いにくい。私がやりたいのはA∩Bです。でもキーワード検索だと入力した単語に当てはまる記事がすべて引っかかっちゃう。(A∪B)

ベン図
和集合とか積集合って言うんだっけ

「カテゴリ名」+「タグA」、「タグA」+「タグB」の検索がしたい。単一カテゴリ・タグの絞り込みは標準でできるけどもっと絞りこみたい。しかもキーボード使いたくない。

不動産とか旅行予約サイトとかはそんな感じで絞り込み機能が付いてる気がするからできないわけがなかろう、と調べたらフォーム機能のチェックボックスを使う必要があるようです。空白で区切って検索で自動的にAND検索になったら簡単なのになー。

フォームを作ってチェックボックスを設置

まずはフォームを作ります。
中身はWordPress側のカテゴリとタグに対応するチェックボックス。あとキーワード検索の部分。クラス名とかidとかnameはそれぞれの環境に合わせて付けます。以下のコードではエスケープ処理などは省いています。

<form role="search" method="get" id="nav_searchform" action="<?php echo home_url('/'); ?>">
    <ul>
        <?php
        //カテゴリのパラメータを指定
        $category_args = array(
            'hide_empty' => 0, //投稿がない場合も取得(任意)
            'orderby' => 'id', //id順(任意)
            'order' => 'ASC' //昇順(任意)
        );
        $categories = get_terms('category', $category_args);;

        foreach ($categories as $category) :
        ?>
        <li>
            <label>
                <input type="checkbox" value="<?php echo esc_attr($category->slug); ?>" name="get_cats[]">
                <?php echo esc_html($category->name); ?>
            </label>
        </li>
        <?php endforeach; ?>
        <?php
        //タグのパラメータを指定
        $post_tag_args = array(                    
            'orderby' => 'count', // タグ名順で指定(任意)                  
            'order' => 'DESC' // 昇順で指定(任意)
        );
        $post_tags = get_terms('post_tag', $post_tag_args);

        foreach ($post_tags as $post_tag) : ?>
        <li>
            <label>
                <input type="checkbox" name="get_tags[]" value="<?php echo esc_attr($post_tag->slug); ?>">
          <?php echo esc_html($post_tag->name); ?>
            </label>
        </li>
        <?php endforeach; ?>

    </ul>
    <div class="search-area">
        <input type="text" placeholder="キーワードを入力" class="" value="<?php esc_attr(the_search_query()); ?>" name="s" id="s">
        <button type="submit" value="">検索する</button>
    </div>
</form>

フォームを装飾する

フォームを作ったらCSSで装飾。チェックボックスのデフォルトデザインをリセットして新しくスタイルを当てたりjQueryで何やかんやしたりしてます。別の話になるので今回は割愛。この辺はまた別の記事でまとめておかないと忘れそうだ……。

3ステップで目的の記事にたどり着きたいよね

functions.phpにsql文

function my_custom_search($search, $wp_query)
{
    global $wpdb;

    if (!$wp_query->is_search)
        return $search;
    if (!isset($wp_query->query_vars))
        return $search;

    $search_words = explode(' ', isset($wp_query->query_vars['s']) ? $wp_query->query_vars['s'] : '');

    if (count($search_words) > 0) {
        $search = '';

        foreach ($search_words as $word) {
            if (!empty($word)) {

                $search_word = '%' . esc_sql($word) . '%';
                $search .= " AND (
					{$wpdb->posts}.post_title LIKE '{$search_word}'
					OR {$wpdb->posts}.post_content LIKE '{$search_word}'
					OR {$wpdb->posts}.ID IN (
						SELECT distinct tr.object_id
						FROM {$wpdb->term_relationships} AS tr
						INNER JOIN {$wpdb->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
						INNER JOIN {$wpdb->terms} AS t ON tt.term_id = t.term_id
						WHERE t.name LIKE '{$search_word}'
					OR t.slug LIKE '{$search_word}'
					OR tt.description LIKE '{$search_word}'
						)
					OR {$wpdb->posts}.ID IN (
						SELECT distinct post_id
						FROM {$wpdb->postmeta}
						WHERE meta_value LIKE '{$search_word}'
						)
					) ";
            }
        }
    }

    return $search;
}
add_filter('posts_search', 'my_custom_search', 10, 2);

search.phpで値を受け取る

検索結果を受け取るsearch.phpで記事を絞り込んで表示させます。tax_queryに選択したカテゴリやタグを多次元配列で入れたら動きました。シンプル。

<?php
    $s = esc_html($_GET['s']); //キーワード
    $get_cats = $_GET['get_cats']; //カテゴリ
    $get_tags = $_GET['get_tags']; //タグ
    //カテゴリーがある場合
    if ($get_cats) {
        $tax_ary[] = array(
        'taxonomy' => 'category',
        'field' => 'slug',
        'terms' => $get_cats,
        'operator' => 'AND'//AND検索。
        );
    }
    //タグがある場合
    if ($get_tags) {
        $tax_ary[] = array(
        'taxonomy' => 'post_tag',
        'field' => 'slug',
        'terms' => $get_tags,
        'operator' => 'AND'//AND検索。
        );
    }
  //ページャーを使う場合の指定。任意。
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;

    $my_query = new WP_Query(array(
        'posts_per_page' => 9, //ページあたりの取得件数(任意)
        'paged' => $paged,
        'post_type' => 'post',
        'tax_query' => $tax_ary, //ここでカテゴリとタグを絞り込み
        'relation' => 'AND', //AND検索。大事。
        's' => $s,
    ));
?>

<?php
    if ($my_query->have_posts()) : ?>
    <ul>
        <?php while ($my_query->have_posts()) : $my_query->the_post(); ?>
        <?php //ここで取得した記事をループさせる記述 ?>
        <?php endwhile; ?>
    </ul>
    <?php else : ?>
    <p>記事が登録されていません</p>
<?php endif; ?>
<?php wp_reset_query(); ?>

これで記事が探しやすくなりました。たとえ駄文だとしても。

肝心の記事がまだ少ない

今回は検索フォームを使う方法を取りましたが、別にそこにこだわらずともいろいろな方法があると思います。面白いなあ。

  • 英語の副題の話
    2022-03-06

    英語の副題はカスタムデータで入れとこ

  • code
    2022-03-02

    プラグインに関するメモ

  • mappin
    2022-03-07

    個人サイト・サーバー選び編

Search

Author

管理人:ナツ

WEB制作等で糊口をしのいでいる限界フリーランス。調べたことなどを備忘録としてまとめたり普通の日記を書いたりしています。デザインとBlenderは趣味。