プラグインを使わず、「カスタムタクソノミー」で「絞り込み検索」する

##やりたいこと
投稿タイプpost内に限定した検索機能とは別に、特定のカスタム投稿タイプ内で、「カスタムタクソノミー」による絞り込み検索機能を、プラグインを使わずに作る！

##状況
- 投稿タイプpost内に限定した検索機能が存在する。（←これに影響しないようにする）
- カスタム投稿タイプ「test」と、付随するカスタムタクソノミーは、functions.phpで追加済み。


カスタムタクソノミーによる検索機能を付けるのにかなり苦労しました。自分が引っかかった部分を中心に、自分のための備忘録且つ、私のような初心者の方のお役に立てる情報になればと思い書きました。おかしな点があれば、ご指摘いただけると嬉しいです。



#1）検索フォーム（searchform-test.php）を作成・設置する。
検索フォームを作成します。
通常はsearchform.phpですが、今回は別ファイルsearchform-test.php（ファイル名は何でもOKです）を用意します。

searchform.phpを呼び出すとき`<?php get_search_form(); ?>`を設置しますが、searchform-test.phpを呼び出せません。
その代わりに、

```php:
require_once('searchform-test.php'); 
```
を設置しました。これで、サイト内の好きな場所で検索フォームを呼び出せます。
（*ちなみに、get_templateでは上手くいきませんでした。）



searchform-test.phpに以下コードを記入します。

```php:searchform-test.php
<form role="search" method="get" action="<?php echo esc_url(home_url('/')); ?>">
  <input type="hidden" name="post_type" value="test">
  <input type="search" class="hidden-search-field" name="s" value="<?php echo get_search_query(); ?>" />
　<input type="submit" value="検索する">
</form>
```

* 2行目のvalueは、自分のカスタム投稿タイプ名のスラッグに置き換えてください。この一行を記入することで検索したい投稿タイプを指定できます。

* `<form>`ですが、`</form>`で閉じることをおすすめします！なぜなら、同ページ内に検索対象が別の検索フォームを設置した時に、互いに影響しあうからです。（←2つ目の検索フォームが動かず、半日ハマりました・・・閉じてないサイト結構多いです）

* 3行目は、「キーワード検索しないから不要だろう」と考えて」いましたが、3行目がないとデータの受け渡しが出来ませんでした。理由は、以下の通り。
> 送信される唯一のパラメータは s で、現在の検索クエリの値を持ちます。<br>by （[get search form](http://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/get_search_form)）

ちなみに、wordpressの管理情報にログインした状態だと、パラメータsを意図的に入れなくても、管理バー内のパラメータsで検索機能がいい感じに動いてしまうことがあるので、注意です！

* 入力エリアが不要な時は、cssで`display:none;`しましょう。

---

次に、以下の検索項目のコードを追加します。
項目は必要な分だけ増やしてください。

```php:searchform-test.php
<table>
 <tr>
   <th>サイズ</th>
    <td>
    //「指定なし」の　ラジオボタンは、個別で用意。
      <label><input type="radio" name="post_tag_speed[]" value="size-nochoice" checked="checked">指定なし</label>
    //その他選択肢であるタームは、配列で取得。
      <?php
        $taxonomy_name = 'size-cat'; // 取得したいタクソノミーの情報
        $args = array( 'hide_empty' => 0 ); // 紐づく投稿記事がなくても表示する
        $taxonomys = get_terms($taxonomy_name,$args);
        if( !empty( $taxonomys ) && !is_wp_error( $taxonomys ) ):
         foreach($taxonomys as $taxonomy):
      ?>
      <label><input type="radio" name="post_tag_size[]" value="<?php echo $taxonomy->slug; ?>"><?php echo $taxonomy->name; ?></label>
        <?php endforeach; ?>
      <?php endif;?>
    </td>
  </tr>
</table>
```
##2)検索結果ページ（search-test.php）の作成
検索結果を表示するためのページを作成します。
通常は、search.phpというテンプレートで作成しますが、カスタム投稿タイプ専用の検索結果ページを作成したい場合は、**search-{投稿タイプ名}.php** というファイルを作り、以下のコードをfunctions.phpに記入します。（*testの部分は置き換えてください）

```php:functions.php
add_filter('template_include','custom_search_template');
function custom_search_template($template){
  if ( is_search() && is_post_type_archive('test') ){
      $post_types = get_query_var('test');
      foreach ( (array) $post_types as $post_type )
        $templates[] = "search-test.php";
      $templates[] = 'search.php';
      $template = get_query_template('search',$templates);
  }
  return $template;
}
```
- `is_search`により検索結果ページが、`is_post_type_archive('test')`により投稿タイプ「test」のアーカイブページが表示されるとき、search-test.phpが呼び出されるようになります。

- ~~だたし注意しておきたいのは、3行目の`is_search()`の部分です。今の状態では、search.phpを表示すべき既存の検索機能の検索結果ページも、影響を受けてしまいす・・・~~
→searchform-test.phpの`<form>`を閉じることで解決！

---
以下のコードを記入し、データを取得します。

```php:search-test.php
<?
$post_type = $_GET['post_type'];
$s = $_GET['s'];
$post_tag_size = $_GET['post_tag_size'][0]; //ラジオボタンのデータ
$post_tag_color = $_GET['post_tag_color']; //チェックボックスのデータ

//ラジオボタンのデータ
if ($post_tag_size !== "size-nochoice"){ //「指定なし」ではなかったら （valueで比較）
  $taxquery_size = array(
     'taxonomy' => 'size-cat',
     'terms' => $post_tag_size, //選択されたタームを取得
     'field' => 'slug',
     );
   }
//チェックボックスのデータ
if (isset($post_tag_color)){ //データがあったら
  $taxquery_color = array(
     'taxonomy' => 'color-cat',
     'terms' => $post_tag_color, //選択されたタームを取得
     'field' => 'slug',
     );
   }
$args = array(
  'post_type' => 'test',
  'paged' => get_query_var('paged'), //注意２：ページ送りを機能させる
  'tax_query' => array(
    'relation' => 'AND', //注意3:タクソノミー検索条件が１つのときは記入しない！
    $taxquery_size,
    $taxquery_color
    ),
  );
query_posts( $args );
?>
<h1>検索結果：<?php echo $wp_query->found_posts; ?>件</h1>

<?php if (have_posts()) : ?>
  <?php while (have_posts()) : the_post(); ?>
    <?php get_template_part('content', 'test'); ?>
  <?php endwhile; ?>
<?php endif; ?>
```
- 上記のコードの通り、検索条件がチェックボックスがラジオボタンかで検索条件取得方法が変わります。

- 未チェックの場合は、検索条件`NULL`になります。特に指定はしていませんが、全検索されます。

- 重要なのは、`tax_query`で、タクソノミー検索条件をパラメータにしてくれます。

---
- 【注意1】3行目は、配列[0]を指定してください。指定しないと、配列になって渡ってくるので、valueの比較ができず条件分岐を通過しないので、上手く動きません。

- 【注意2】`query_posts`は、現在のメインクエリの上書きしてしまうので、'page' クエリ変数で現在のページ送り番号を取得し、 WordPressにどのページを表示するかを伝える必要があります。検索結果のページ送りを正しく動作させるために、`query_posts`内で`'paged' => get_query_var('paged')`を記述します。そうでないと、2ページ目以降が1ページ目の内容と同じ・・・なんてことになります。

- 【注意3】1つしかタクソノミー検索条件を含まない場合は、`tax_query`の`relation`は指定しないでください。codexにも以下の通り記載がありました。（上のコードは例として記入しています）
>relation (文字列) - 2つ以上のタクソノミー検索条件（内側の配列）が含まれる場合に、それらの論理的な関係を指定します。有効な値は 'AND' または 'OR' です。1つしかタクソノミー検索条件を含まない場合は指定しないでください。デフォルトは 'AND' です。



以上で、絞り込み検索機能は完成です。

~~ただ、2)で登場した条件分岐`is_search()`による既存の検索結果への影響が解消されていません。それについて、私は以下の方法で解決しました。（正しいかは自信がありません）~~
→searchform-test.phpの`<form>`を閉じることで解決！



##3)応用編：記事の並び順を指定する

```php:functions.php
//投稿タイプ「post」の並び順
add_post_type_support( 'post', 'page-attributes' );
function change_posts_query( $query ) {
  if( $query->is_main_query() && !is_admin() ) {
    if (!is_post_type_archive('test') ){//投稿タイプ「test」のアーカイブでは無かったら
      if($query->is_category() || $query->is_tag() || $query->is_archive()|| $query->is_search() ) {
        $query -> set( 'post_type', 'post' ); // 投稿タイプ「post」を指定
        $query -> set( 'orderby', 'menu_order date' ); 
        $query -> set( 'order', 'DESC' ); 
      }
    }
  }
}
add_action( 'pre_get_posts', 'change_posts_query' );

//投稿タイプ「test」の並び順
add_post_type_support( 'test', 'page-attributes' );
function change_posts_test_query( $query ) {
  if(!is_admin() ) {
    if (is_post_type_archive('test') ) { //投稿タイプ「test」のアーカイブだったら
      if($query->is_search()|| $query->is_archive() ) {
        $query -> set( 'post_type', 'test' ); // 投稿タイプ「test」を指定
        $query -> set( 'orderby', 'menu_order' ); 
        $query -> set( 'order', 'DESC' ); 
      }
    }
  }
}
add_action( 'pre_get_posts', 'change_posts_test_query' );
```

上記のコードの通り、投稿タイプ「post」と「test」それぞれで降順を指定します。

特に重要な点は、以下の３点です。
1. **`!is_admin()`で管理画面を除外すること。**
記入が漏れると管理画面の記事一覧に影響が出ます。
2. `is_post_type_archive('post')`という表記はできないので、**`!is_post_type_archive('test')`** という分岐にする。
3. 投稿タイプ「test」の並び順の条件分岐に、`$query->is_main_query()`を入れると正しく動きません。

##〜参考サイト〜
https://hirashimatakumi.com/blog/3109.html
http://blog.keisuke11.com/webdesign/search-form-custom-post/
https://sole-color-blog.com/blog/265/