【Wordpress】カスタム投稿をカテゴリーと投稿日で絞り込む機能を自作する

## 完成イメージ
投稿日とカテゴリー２つの条件で投稿を絞り込むことができます。
それぞれ片方でも、両方の条件で複合的に絞り込むこともできます。

![画面収録 2024-09-15 19.42.56 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783222/caed83b4-22bf-e139-b8ef-7acf59a1f272.gif)


## 実装のポイント
wordpressを多少理解できている前提で、この実装の肝となるポイントを先にご紹介します。

-   **サブクエリに「```date_qeury```」と「```tax_query```」という2つの条件を設定する**
-  **検索ボタンを押した際に、GETメソッドで上記2つのクエリに必要な投稿日とカテゴリーの情報をURLパラメータから取得する**


## 前提条件・準備
- 実装する検索機能についてはプラグインを使用せず自作する
- カスタム投稿、カスタムタクソノミーはAdvanced Custom Fieldsを使用する
- カスタム投稿タイプは「news」、カスタムタクソノミーのスラッグは「news_category」とする

また、ダミーの投稿を数件作成してカテゴリーを設定しておきます。
![スクリーンショット 2024-08-18 21.13.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783222/37fec4bb-13d1-0261-6fee-5e3f7b2e04f5.png)

投稿日についても投稿後に変更することができるため、適当に設定しておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783222/44e753ca-2741-b82b-ae9f-f0b6481d7485.png)


## 画面内の要素の説明
絞り込み機能の仕組みについて理解するため、先に画面内の要素について整理しておきます。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f333738333232322f61656566666636662d616532662d323065382d346466392d3234623865303232336131372e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783222/6d271187-1fa2-01a7-3069-296e7791ea74.png)

1. 指定した投稿タイプの投稿年月を取得してセレクトボックスの選択肢に設定
1. 投稿に設定したカテゴリーを取得してラジオボタンの選択肢に設定
1. 1と2で選択した値をformで送信
1. 3で送信した情報によって絞り込まれた投稿の一覧（＝検索結果）


## 実装
では順番に実装していきます。
カスタム投稿タイプnewsの一覧ページの想定で、archive-news.phpを作成します。

### 検索ボタン
 先に`<form>`要素を用意しておきましょう。
 この中にセレクトボックスとラジオボタンを設置して、検索ボタンを押下した際に投稿日とカテゴリーの情報を送信します。

```php:archive-news.php
<form action="<?php echo esc_url(home_url('/news')); ?>" method="get">
　　　　　<!-- ここにセレクトボックスとラジオボタンを挿入 -->
     
  <button type="submit">検索する</button>
</form>
```

formのaction属性には今表示しているアーカイブページのパーマリンクを設定しています。

### セレクトボックス
まずは投稿タイプがnewsの投稿をサブループで取得します。

 ```php:archive-news.php
<?php 
  $args = [
    'post_type' => 'news',
    'posts_per_page' => -1,
    'post_status' => 'publish',
  ];
  $the_query = new WP_Query($args);
```

そして、ここがポイントの1つ目で、すべての投稿を投稿された年・月ごとに配列へ格納します。
```php:archive-news.php
  // 投稿年月ごとに投稿を配列に格納
  if ($the_query->have_posts()) {
    while ($the_query->have_posts()) {
      $the_query->the_post();
      $archive_list[ get_the_time('Y/m', $post->ID)][] = $post->post_title;
    }
  }
  wp_reset_postdata();
  ?>
```
var_dumpで確認するとイメージしやすいです。

多次元配列になっており、2024/08、2024/07、2024/06という投稿年月のキーに対して、投稿が配列で格納されていることがわかります。

```php
echo '<pre>';
var_dump($archive_list);
echo '</pre>';

/* 実行結果
array(3) {
  ["2024/08"]=>
  array(3) {
    [0]=>
    string(30) "カテゴリー3　2024年8月"
    [1]=>
    string(30) "カテゴリー2　2024年8月"
    [2]=>
    string(30) "カテゴリー1　2024年8月"
  }
  ["2024/07"]=>
  array(1) {
    [0]=>
    string(30) "カテゴリー1　2024年7月"
  }
  ["2024/06"]=>
  array(1) {
    [0]=>
    string(30) "カテゴリー1　2024年6月"
  }
}
*/
```

これで投稿年月が取得できたので、これをセレクトボックスで使用すればOKです！
```php:archive-news.php
<!-- 投稿年月のセレクトボックス -->
<?php 
if ($archive_list) : ?>
<select name="publish_date">
  <option value="">選択してください</option>
  <?php foreach ($archive_list as $year_month => $archive) :
    $year_month_arr = explode('/', $year_month);
    $year = $year_month_arr[0];
    $month = $year_month_arr[1];
    $formatted_date = $year.$month;
  ?>
  <option value="<?php echo $formatted_date; ?>"
    <?php echo (isset($_GET['publish_date']) && $_GET['publish_date'] === $formatted_date) ? 'selected' : ''; ?>>
    <?php echo $year.'年'.$month.'月'; ?>
  </option>
  <?php endforeach; ?>
</select>
<?php endif; ?>
```
便宜上「2024年08月」という表記にしたいため多少コードがごちゃついていますが、この辺りは好きに書いて大丈夫です。

foreachで`<option>`をループで生成。
また、ループ外で「選択してください」という投稿年月を指定しない選択肢も用意しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783222/bed259c5-e6da-7892-25cc-3ad7787bf882.png)


### ラジオボタン
基本的にはセレクトボックスと一緒です。
最初の`<label>`は「すべて」という選択肢で、「カテゴリーを選択しない」と同義です。
カスタムタクソノミー「news_category」を`get_terms()`というテンプレート関数で取得しています。

```php:archive-news.php

<!-- カテゴリーを取得 -->
<div class="category-field">
  <label>
    <input
      type="radio"
      name="news_category"
      value=""
      <?php echo (!isset($_GET['news_category']) || $_GET['news_category'] === '') ? 'checked' : ''; ?>
    >
    すべて
  </label>
    
  <?php 
  $terms = get_terms([
      'taxonomy' => 'news_category',
  ]);

  if (!is_wp_error($terms) && !empty($terms)) : 
  ?>
  <?php foreach ($terms as $term) : ?>
    <label>
      <input 
        type="radio" 
        name="news_category" 
        value="<?php echo esc_attr($term->slug); ?>"
        <?php echo (isset($_GET['news_category']) && $_GET['news_category'] === $term->slug) ? 'checked' : ''; ?>
      >
      <?php echo esc_html($term->name); ?>
    </label>
  <?php endforeach; ?>
<?php endif; ?>
</div>

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783222/b722a032-fc92-364c-35aa-9da10a344d68.png)


### 投稿の表示
ではいよいよ投稿の絞り込み表示の部分です。
まずは$_GETでURLパラメータから値を取得し変数に格納します。

ページネーションを追加したいならここでpagedを追加して、絞り込み機能とページネーションを両立させることも可能です。
```php:archive-news.php
<?php
$selected_category = isset($_GET['news_category']) ? $_GET['news_category'] : '';
$selected_date = isset($_GET['publish_date']) ? $_GET['publish_date'] : '';

$args = array(
    'post_type' => 'news',
    'posts_per_page' => 10,
    'post_status' => 'publish'
);
```

そして、ようやく冒頭のポイントの**tax_query**と**date_query**が登場しました！
どちらもWP_Queryで使えるもので、引数の配列にタクソノミーや日付を指定することで、クエリに条件を設定し、投稿を絞り込むことができます。

```php:archive-news.php
// カテゴリの絞り込み条件をクエリに設定
if ($selected_category !== '') {
  $args['tax_query'] = [
    [
      'taxonomy' => 'news_category',
      'field'    => 'slug',
      'terms'    => $selected_category
    ]
  ];
}
// 年月の絞り込み条件をクエリに設定
if ($selected_date) {
  $year = substr($selected_date, 0, 4);
  $month = substr($selected_date, 4, 2);
  
  $args['date_query'] = [
    [
      'year'  => $year,
      'month' => $month,
    ]
  ];
}
```

最後にいつも通りループで投稿を取得すればOK！
```php:archive.php
// 条件が設定されたループを実行
$the_query = new WP_Query($args);

if ($the_query->have_posts()): 
    while ($the_query->have_posts()): 
        $the_query->the_post();
        the_title();
        echo '<br>';
    endwhile;
    wp_reset_postdata();
else: 
    echo '投稿が見つかりませんでした。';
endif;
```

投稿年月・カテゴリーそれぞれと両方で絞り込みできることが確認できます！

![画面収録 2024-09-15 19.42.56 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783222/2a595914-9f52-3ab0-319a-4acfa694e048.gif)


## 参考
- [Date Parameters](https://developer.wordpress.org/reference/classes/wp_query/#date-parameters)
- [Taxonomy Parameters](https://developer.wordpress.org/reference/classes/wp_query/#taxonomy-parameters)