© 2021 Addapter Inc.

BLOG

【WP6.2対応】プラグイン無しでWordPressの投稿タイプに絞込検索を実装する方法

コーディング2021/07/30
blog author
コソエガワ
blog thumbnail

目次

こんにちは、Wordpress大好きな代表の小副川です。

クライアントワークでWordpressを使っていると、カスタム投稿タイプを追加して実績の一覧・詳細ページを作ったり、カスタムタクソノミーやカスタムフィールドを追加したりと、カスタム◯◯を色々弄り倒す必要が出てきます。

さらにそのカスタム◯◯を一覧表示する際に、絞り込み検索機能を実装したい、というニーズは結構多いのではないでしょうか?

しかし「Wordpress 絞り込み検索」とかでググってみても、大抵プラグインを紹介している記事か、「プラグイン無しで」と書いてる記事でも、かなり煩雑な感じのコードが出てきたりと、google先生には珍しくいまいちイケてない検索結果となっています。

そこで今回は、Wordpressを知り尽くした私が、最もWordpressの仕様をフル活用した絞り込み検索機能のベストプラクティスをご紹介したいと思います!

ちょうど弊社の制作実績で使ってまして、完成すると以下のような形で自由に検索項目を指定することが可能になります。

【絞り込み検索の実例】
株式会社ウイングドア様 開発実績ページ

コピペで使えるPHPコード

まずコピペ用コードから。編集するファイルは2つ。
アーカイブページ表示用のarchive.php(カスタム投稿タイプのアーカイブの場合はarchive-◯◯.php)と関数定義用のfunctions.phpです。

archive.php(archive-◯◯.php)

<!-- フォームパーツ -->
<form action="<?=esc_url(home_url('/アーカイブページのパス/'))?>">

  ■ テキストボックス<br>
  <input type="text" name="foo" value="<?=esc_html(get_query_var('foo'))?>">
  
  ■ セレクトボックス<br>
  <select name="bar">
    <option value="選択肢1"<?=get_query_var('bar')==='選択肢1'?' selected':''?>>選択肢1</option>
    <option value="選択肢2"<?=get_query_var('bar')==='選択肢2'?' selected':''?>>選択肢2</option>
    <option value="選択肢3"<?=get_query_var('bar')==='選択肢3'?' selected':''?>>選択肢3</option>
  </select>

    ■ ラジオボタン<br>
  <label><input type="radio" name="baz" value="選択肢1"<?=get_query_var('baz')==='選択肢1'?' checked':''?>> 選択肢1</label>
  <label><input type="radio" name="baz" value="選択肢2"<?=get_query_var('baz')==='選択肢2'?' checked':''?>> 選択肢2</label>
  <label><input type="radio" name="baz" value="選択肢3"<?=get_query_var('baz')==='選択肢3'?' checked':''?>> 選択肢3</label>
  
  ■ チェックボックス<br>
  <label><input type="checkbox" name="qux[]" value="選択肢1"<?=in_array('選択肢1',get_query_var('qux'))?' checked':''?>> 選択肢1</label>
  <label><input type="checkbox" name="qux[]" value="選択肢2"<?=in_array('選択肢2',get_query_var('qux'))?' checked':''?>> 選択肢2</label>
  <label><input type="checkbox" name="qux[]" value="選択肢3"<?=in_array('選択肢3',get_query_var('qux'))?' checked':''?>> 選択肢3</label>

  <?php wp_nonce_field('my-archive-nonce', 'nonce'); ?>
  <button type="submit">検索する</button>
  
</form>


<!-- ループ処理 -->
<?php if(have_posts()) : while(have_posts()): the_post(); ?>
  <?php the_title(); ?>
<?php endwhile; endif;?>

functions.php

<?php

////////////////////////////////
// getの値を追加
////////////////////////////////

function add_query_vars_filter( $vars ){
  $vars[] = "foo";
  $vars[] = "bar";
  $vars[] = "baz";
  $vars[] = "qux";
  return $vars;
}
add_filter( 'query_vars', 'add_query_vars_filter' );


////////////////////////////////
// アーカイブページにクエリを追加
////////////////////////////////
add_action( 'pre_get_posts', 'add_archive_custom_query' ); // pre_get_postsにフック
// フック時に使う関数
function add_archive_custom_query( $query ) {
  if ( ! is_admin() && $query->is_main_query() && is_post_type_archive('カスタム投稿タイプのスラッグ') ) {

    // nonce検証
    $nonce = $_REQUEST['nonce'];
    if(!wp_verify_nonce($nonce, 'my-archive-nonce')) {
      die();
    }

    // GETの引数を取得
    $get_foo = get_query_var('foo');
    $get_bar = get_query_var('bar');
    $get_buz = get_query_var('buz');
    $get_qux = get_query_var('qux');
    

    // 全文検索
    if(!empty($get_foo)) {
      $query->set('s', $get_foo);
    }

    // meta_query を追加

    $meta_query = array(
      'relation' => 'AND'
    );
    // セレクトボックス
    if(!empty($get_bar)) {
      array_push($meta_query, array(
        'key' => 'bar', // metaキー
        'value' => $get_bar, // 検索値
        'compare' => '=' // 一致
      ));
    }
    // ラジオボタン
    if(!empty($get_buz)) {
      array_push($meta_query, array(
        'key' => 'buz',
        'value' => $get_buz,
        'compare' => '='
      ));
    }
    // チェックボックス
    if(!empty($get_qux)) {
      array_push($meta_query, array(
      'key' => 'qux',
      'value' => $get_qux,
      'compare' => 'LIKE' // チェックボックスの場合はLIKE検索になるので注意
      ));
    }

    $query->set('meta_query', $meta_query);

    // 検索やmeta_query以外にも、authorやカスタム投稿タイプ、カテゴリー、タクソノミーなど
    // WP_Queryの各種パラメーターが使えます
    // その他のクエリパラメータは以下参照下さい
    // http://notnil-creative.com/blog/archives/1288

  }
}

何をしているか

archive.php

まずarchive.phpの中で、

  • フォームパーツの作成
  • メインループ(出力)処理

を行っています。一つづつ見ていきたいと思います。

フォームパーツの作成

まずformタグですが、actionにアーカイブページのパスを渡すのがポイントです。esc_url() でエスケープ処理、home_url()でURL取得を行っています。

<form action="<?=esc_url(home_url('/アーカイブページのパス/'))?>">
</form>

次にinputタグselectタグ周りです。

  ■ テキストボックス<br>
  <input type="text" name="foo" value="<?=esc_html(get_query_var('foo'))?>">
  
  ■ セレクトボックス<br>
  <select name="bar">
    <option value="選択肢1"<?=get_query_var('bar')==='選択肢1'?' selected':''?>>選択肢1</option>
    <option value="選択肢2"<?=get_query_var('bar')==='選択肢2'?' selected':''?>>選択肢2</option>
    <option value="選択肢3"<?=get_query_var('bar')==='選択肢3'?' selected':''?>>選択肢3</option>
  </select>

    ■ ラジオボタン<br>
  <label><input type="radio" name="baz" value="選択肢1"<?=get_query_var('baz')==='選択肢1'?' checked':''?>> 選択肢1</label>
  <label><input type="radio" name="baz" value="選択肢2"<?=get_query_var('baz')==='選択肢2'?' checked':''?>> 選択肢2</label>
  <label><input type="radio" name="baz" value="選択肢3"<?=get_query_var('baz')==='選択肢3'?' checked':''?>> 選択肢3</label>
  
  ■ チェックボックス<br>
  <label><input type="checkbox" name="qux[]" value="選択肢1"<?=in_array('選択肢1',get_query_var('qux'))?' checked':''?>> 選択肢1</label>
  <label><input type="checkbox" name="qux[]" value="選択肢2"<?=in_array('選択肢2',get_query_var('qux'))?' checked':''?>> 選択肢2</label>
  <label><input type="checkbox" name="qux[]" value="選択肢3"<?=in_array('選択肢3',get_query_var('qux'))?' checked':''?>> 選択肢3</label>

各inputのnameの値は何でも良いですが、カスタムフィールドの場合はslug名、タクソノミーの場合はタクソノミーのslug名など、分かりやすいものにしておくと良いでしょう。

チェックボックスの場合は配列になるためnameが「キー[]」のような形式になる点注意してください。

また、検索値がそれぞれの値にセットされるように以下のような記述をしています。

テキスト
value="<?=esc_html(get_query_var('foo'))?>"

セレクトボックス
<?=get_query_var('bar')==='選択肢1'?' selected':''?>

ラジオボタン
<?=get_query_var('baz')==='選択肢1'?' checked':''?>

チェックボックス
<?=in_array('選択肢1',get_query_var('qux'))?' checked':''?>

get_query_var(“キー”)で、URLに「foo=aaa&bar=bbb」のような形でセットされたパラメーターが取得できます。

$_GET[‘foo’]でも取得できますが、get_query_var()はwordpressが用意してくれてる関数なのでこちらを使用したほうが安全だと思います。

<?php wp_nonce_field('my-archive-nonce', 'nonce'); ?>

最後にセキュリティ対策として、nonceフィールドというのを出力します。
my-archive-nonceはなにか別の文字列に変えて下さい。

ちなみに上記の検索フォームはアーカイブページではなく、ヘッダーやサイドバー等の共通パーツでも、他のページでも動きます。(<form action>がアーカイブページを指定してあればOK)

メインループ(出力)処理

これは通常のアーカイブページと何ら変わりありません。
いわゆるwordpressのメインループの書き方でokです。

次にfunctions.phpに記載したコードを解説していきます。

functions.php

functions.php の中では、

・不正なアクセスのチェック

・GETの引数を追加

・クエリの追加

を行っています。こちらも一つずつみていきましょう。

不正アクセスのチェック

以下のif文で、管理画面ではなくカスタム投稿タイプのアーカイブページか、きちんと指定のフォームから送信されてきた情報か、などを検証しています。

if ( ! is_admin() && $query->is_main_query() && is_post_type_archive('カスタム投稿タイプのスラッグ') ) {

    // nonce検証
    $nonce = $_REQUEST['nonce'];
    if(!wp_verify_nonce($nonce, 'my-archive-nonce')) {
      die();
    }

GETの引数を追加

WordPressはセキュリティの関係上、予約後以外のGETの引数を認めていないため、別途使用したい場合は個別に追加する必要があります。

////////////////////////////////
// getの値を追加
////////////////////////////////

function add_query_vars_filter( $vars ){
  $vars[] = "foo";
  $vars[] = "bar";
  $vars[] = "baz";
  $vars[] = "qux";
  return $vars;
}
add_filter( 'query_vars', 'add_query_vars_filter' );

これは上記のようにquery_varsフィルターに、GETキーの一覧を配列sw渡してあげればokです。このばあいは、foo・bar・baz・quxが引数として機能するようになります。これで$_GETでもget_query_var関数でも取得できます。

クエリの追加

次にアーカイブページにクエリを追加します。ここが特にポイントです。

まずwordpressの動きとして、通常アーカイブページを開いた際は、以下のようなクエリが発行された状態になります。(わかりやすくWP_Query形式で書きます)

$args = array('post_type'=>'blog'); // カスタム投稿タイプblogの場合
$query = new WP_Query($args);

これをwhile(have_posts())でループさせてるイメージなのですが、
pre_get_postsフックを使うと、このクエリに好きな条件を追加することができるようになります。(なんかこれ知ってると万能感感じませんか?僕だけですかね?笑)

それが以下のコードになるのですが、よく分からないと思うので一つずつ解説してきます。

    // 全文検索
    if(!empty($get_foo)) {
      $query->set('s', $get_foo);
    }

    // meta_query を追加

    $meta_query = array(
      'relation' => 'AND'
    );
    // セレクトボックス
    if(!empty($get_bar)) {
      array_push($meta_query, array(
        'key' => 'bar', // metaキー
        'value' => $get_bar, // 検索値
        'compare' => '=' // 一致
      ));
    }
    // ラジオボタン
    if(!empty($get_buz)) {
      array_push($meta_query, array(
        'key' => 'buz',
        'value' => $get_buz,
        'compare' => '='
      ));
    }
    // チェックボックス
    if(!empty($get_qux)) {
      array_push($meta_query, array(
      'key' => 'qux',
      'value' => $get_qux,
      'compare' => 'LIKE' // チェックボックスの場合はLIKE検索になるので注意
      ));
    }

    $query->set('meta_query', $meta_query);

まず、pre_get_postsアクションフックにadd_actionで関数を登録します。

ちなみにアクションフックというのは、wordpressの処理の途中に別の関数を割り込ませるポイントの事で、wordpressには多数用意されています。一方で出力結果のHTMLを整形したりする際は、フィルターフックという改変ポイントも用意されてます。長くなるので詳しく知りたい方はググって下さい。

add_action( 'pre_get_posts', 'add_archive_custom_query' ); // pre_get_postsにフック
// フック時に使う関数
function add_archive_custom_query( $query ) {
}

pre_get_postsというのは、投稿一覧をwordpressで取得する直前に実行されるフックなので、この時点で色々とクエリを追加しておけば、投稿一覧が思いのままに操れるというわけです。

add_archive_custom_query関数の中身を見ていきます。(add_archive_custom_queryは適当な関数名なので、投稿タイプ名とかに合わせて変更してokです)

 // GETの引数を取得
    $get_foo = get_query_var('foo');
    $get_bar = get_query_var('bar');
    $get_buz = get_query_var('buz');
    $get_qux = get_query_var('qux');

上記でGETの引数として渡された各数値を取得しています。

https://example.com/blog?foo=テスト&bar=あああ

というURLの場合は、fooとbarにそれぞれ「テスト」と「あああ」が格納されます。

そして、その下からいよいよGETの値を元にクエリを追加していきます。
クエリを追加するためには、$queryオブジェクトのsetメソッドを使います。

$query->set('セットしたいキー', 'セットしたい値');

ちなみに今回は関数の第一引数に$queryとしているので$queryなんですが、略して$qとかでもokかと思います。

function add_archive_custom_query( $q ) {
$q->set();
}

追加できるクエリは、WP_Queryやget_posts関数で指定できるものは全部イケルと思います。例えば、投稿者を絞り込むなら

$query->set('author', 1);

とかしとくと、ユーザーID1の人の投稿のみになりますし、2021年の1月の記事のみ出したい場合は、

$query->set('year', 2021);
$query->set('monthnum', 1);

で絞り込めます。

この要領で好きなクエリを追加していけばOK。

使えるパラメーターの一覧は以下のサイトがわかりやすいのでいつも参考にしてます。

WP_Queryの使い方をPHPコードにまとめた便利なコード・スニペット
http://notnil-creative.com/blog/archives/1288

唯一注意点としては、Advanced Custom Fields等のプラグインを使っている場合で、カスタムフィールド(post_meta)からの検索をする場合はmeta_query、もしくはmeta_keyとmeta_valueをセットで使いますが、
チェックボックスのように、複数の値が配列でDBに入っている場合は、compareをLIKEにするのが間違いないです。

// チェックボックス
    if(!empty($get_qux)) {
      array_push($meta_query, array(
      'key' => 'qux',
      'value' => $get_qux,
      'compare' => 'LIKE' // チェックボックスの場合はLIKE検索になるので注意
      ));
    }

    $query->set('meta_query', $meta_query);

配列の中から検索するINも用意されているのですが、配列をDBに格納する場合に、シリアライズという処理をされた実質テキストのような状態で入れられている場合が多いので、格納されてるのはテキストなのに配列として検索する、みたいな状態になってしまい、上手く機能しません。

まとめ

いかがでしたでしょうか?
恐らくカスタム投稿タイプのアーカイブで検索する方法としてはベストプラクティスの最強記事になってると思います。

これを使えば有料のプラグインなんて使う必要無いですし、
自由にカスタマイズできる幅がぐんと広がります。
是非参考にしてみてください。

scroll to Top