WordPressで月別予定一覧を作ったので記録

月ごとのイベント開催カレンダーとかにありがちなやつを作りました。

# 仕様
## ワイヤーフレーム
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69733/1bc8ad12-c785-a0e1-9f11-15124654b2b8.png)
## 投稿一覧

* [月別インデックス]をクリックするとその月が[日程]に含まれる投稿がタブ切り替え方式で表示される
* [日程]は、[開始日][終了日]で設定されている。[開始日]のみの投稿もある。
* 今日以降の[日程]が含まれた投稿が表示される
* 投稿はカスタム投稿タイプで作成する

# 実装
## カスタム投稿タイプ

一般的な方法で作成済みとします。説明は割愛。
スラッグは、"events"にしました。

## カスタムフィールドを作成
* 開始日
 * [フィールドタイプ]…[デイトピッカー]
 * [返り値のフォーマット]…[Custom] → "Y-m-d"
* 終了日
 * 開始日と同じ
 * 但し[必須か？]は[いいえ]にする（開始日のみの投稿もあるので）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69733/2b21261c-785c-13ea-e945-f92a47a4b492.png)

## テンプレートファイル

```php:archive.php
<?php
$today = date('Y-m-d');
// $today = date("Y-m-d", strtotime('2020-01-31')); //for debug
$args = array(
  'post_type'=> 'events',
  'orderby'  => 'meta_value',
  'order'    => 'ASC', //昇順
  'meta_key' => 'evstartdate', //ACFのフィールド名
  'meta_query' => array( // 判定条件…(開始日 >= 今日)or(終了日 >= 今日)
    'relation' => 'OR',
    array(
      'key'     => 'evstartdate',
      'value'   => $today,
      'type'    => 'date',
      'compare' => '>=',
    ),
    array(
      'key' => 'evenddate',
      'value' => $today,
      'type'    => 'date',
      'compare' => '>=',
    ),
  ),
  'posts_per_page' => 0,
);
$query = new WP_Query( $args );
if( $query->have_posts() ) :
  /*================================
  月別投稿リスト取得
  =================================*/
  while ( $query->have_posts() ) : $query->the_post();
    $date1 = date('Y-m-d',strtotime(get_field('evstartdate', false, false)));
    if ( get_field('evenddate') ) {
      // 終了日が設定されている場合
      $date2ym = date('Y-m',strtotime(get_field('evenddate', false, false)));

      // 開始日が今日より前・・今日の月～終了月を設定
      // 開始日が今日以降・・・開始日月～終了月を設定
      $tmpdate = ( $today > $date1 ) ? $today : $date1;

      while( $date2ym >= date("Y-m", strtotime($tmpdate)) ) {
        $eventlist[ date("Y-m", strtotime($tmpdate)) ][] = get_the_ID();
        $tmpdate = date("Y-m-1", strtotime($tmpdate));//月はじめに変換(31日等の場合に月を飛び越える事を防ぐため)
        $tmpdate = date("Y-m-d",strtotime($tmpdate . "+1 month"));//1ヶ月後の月を取得
      }
    } else {
      // 終了日が未設定の場合は、開始日の月のみ設定
      $eventlist[ date("Y-m", strtotime($date1)) ][] = get_the_ID();
    }
  endwhile;
// echo '<pre>';var_dump($eventlist); echo '</pre>';

  if( isset($eventlist) ) :
    /*================================
    月インデックス出力
    =================================*/
?>
    <div class="month-index">
      <ul>
<?php
    foreach( $eventlist as $ym => $archive ) :
      $ym_arr = explode( '-', $ym );
      echo '<li>' . $ym_arr[0].'年'.$ym_arr[1].'月</li>'; // 月インデックス出力
    endforeach;
?>
<?php wp_reset_postdata(); //必須 ?>
        </div>
        <div class="month-content">
          <div class="active">
<?php
    /*================================
    月別投稿リスト出力
    =================================*/
    foreach ($eventlist as $value) :
      $args = array(
        'post_type' => 'events',
        'post__in' => $value,
        'posts_per_page' => 0,
      );
      $query = new WP_Query( $args );
?>
            <ul class="ls-reports">
  <?php while ( $query->have_posts() ) : $query->the_post(); ?>
<?php
    /*================================
    投稿の内容を出力します！
    =================================*/
?>
  <?php endwhile; ?>
            </ul>
          </div>
          <div>
<?php
      wp_reset_postdata(); //必須
    endforeach;
?>
          </div>
        </div>
  <?php else : ?>
        <p style="text-align:center">実施予定は調整中です。</p>
  <?php endif; ?>
<?php else : ?>
        <p style="text-align:center">現在、開催予定のイベントはありません。</p>
<?php endif; ?>

```

## タブ切り替え処理

```js:js
jQuery(function($){
  // 月別投稿 タブ切り替え
  var $index = $('.month-index li');
  var $content = $('.month-content > div');
  $index.eq(0).addClass('active');
  $content.eq(0).addClass('active');

  $index.click(function(){
    $index.removeClass('active');
    $(this).addClass('active');
    $content.removeClass('active');
    $content.eq($(this).index()).addClass('active');
  });
});
```

## SASS
※タブ切り替えに関わる部分のみ

```sass

.month-index {
  ul {
    display: flex;
    flex-wrap: wrap;
    li {
      flex: 0 1 29%;
    }
  }
}
.month-content {
  > div {
    display: none;
    &.active {
      display: block;
    }
  }
}
```

# 説明

タブ切り替えを踏まえ、以下のようなhtmlを作ることにする。

```html

<div class="month-index">
  <ul>
    <li>2020年1月</li>
    <li>2020年2月</li>
  </ul>
</div>
<div class="month-content">
  <!-- 2020年1月の投稿 -->
  <div>
    <ul>
      <li>投稿が入ります</li>
      <li>投稿が入ります</li>
    </ul>
  </div>
  <!-- 2020年2月の投稿 -->
  <div>
    <ul>
      <li>投稿が入ります</li>
      <li>投稿が入ります</li>
    </ul>
  </div>
</div>
```

php では、$eventlistがポイント。

```php

$today = date('Y-m-d');
// $today = date("Y-m-d", strtotime('2020-01-31')); //for debug
$args = array(
  'post_type'=> 'events',
  'orderby'  => 'meta_value',
  'order'    => 'ASC', //昇順
  'meta_key' => 'evstartdate', //ACFのフィールド名
  'meta_query' => array( // 判定条件…(開始日 >= 今日)or(終了日 >= 今日)
    'relation' => 'OR',
    array(
      'key'     => 'evstartdate',
      'value'   => $today,
      'type'    => 'date',
      'compare' => '>=',
    ),
    array(
      'key' => 'evenddate',
      'value' => $today,
      'type'    => 'date',
      'compare' => '>=',
    ),
  ),
  'posts_per_page' => 0,
);
$query = new WP_Query( $args );
if( $query->have_posts() ) :
  /*================================
  月別投稿リスト取得
  =================================*/
  while ( $query->have_posts() ) : $query->the_post();
    $date1 = date('Y-m-d',strtotime(get_field('evstartdate', false, false)));
    if ( get_field('evenddate') ) {
      // 終了日が設定されている場合
      $date2ym = date('Y-m',strtotime(get_field('evenddate', false, false)));

      // 開始日が今日より前・・今日の月～終了月を設定
      // 開始日が今日以降・・・開始日月～終了月を設定
      $tmpdate = ( $today > $date1 ) ? $today : $date1;

      while( $date2ym >= date("Y-m", strtotime($tmpdate)) ) {
        $eventlist[ date("Y-m", strtotime($tmpdate)) ][] = get_the_ID();
        $tmpdate = date("Y-m-1", strtotime($tmpdate));//月はじめに変換(31日等の場合に月を飛び越える事を防ぐため)
        $tmpdate = date("Y-m-d",strtotime($tmpdate . "+1 month"));//1ヶ月後の月を取得
      }
    } else {
      // 終了日が未設定の場合は、開始日の月のみ設定
      $eventlist[ date("Y-m", strtotime($date1)) ][] = get_the_ID();
    }
  endwhile;
```

上記で以下のような配列を作成し、それをもとに月別インデックスと、月別の投稿一覧を生成する。

```
array(5) {
  ["2019-05"]=>
  array(1) {
    [0]=>
    int(2303)
  }
  ["2019-06"]=>
  array(1) {
    [0]=>
    int(2303)
  }
  ["2019-09"]=>
  array(2) {
    [0]=>
    int(2302)
    [1]=>
    int(2301)
  }
  ["2019-12"]=>
  array(3) {
    [0]=>
    int(2318)
    [1]=>
    int(2328)
    [2]=>
    int(2329)
  }
  ["2020-03"]=>
  array(1) {
    [0]=>
    int(2339)
  }
}
```

おわり
もっとスマートな方法があれば教えて下さい。

# 参考サイト
【WordPress】wp_get_archivesを使わずに特定カテゴリーの年月アーカイブリストを表示したい
https://www.rcnt.jp/blog/wordpress-wp_get_archives/