イベント等の開催日を Advanced Custom Fields (ACF) の日付選択フィールドで作成し、この開催日をを使って年月別のアーカイブページを作りたい。そして更に、その月別リストを年単位で開閉表示させたいと考えました。

通常の月別アーカイブの月別リストを年単位で開閉表示させる方法は、「WordPressの月別アーカイブを年単位で開閉させる *Ateitexe」に書かれていましたので、これを参考にさせていただきました。

一方、カスタムフィールドの日付から月別アーカイブを作成する方法は、「カスタムフィールドに入力した日付で月別絞り込み&アーカイブページの整形 | oku-log」等で説明されていますが、今回は月別リストの開閉表示をしたかったので、オリジナルで実装してみることにしました。

まず前提として、カスタムフィールドの年月別のページは日付アーカイブを使わず、固定ページで作成して、URL パラメータを使って対象のイベントの一覧を取得することにします。
https://example.com/event/monthly/?date=202403
のような URL として、date パラメータの値と日付フィールドの値(Ymd の Ym または Y)が一致するイベントを抽出するという感じです。

開閉式月別リストの作成

WordPressの月別アーカイブを年単位で開閉させる *Ateitexe」で紹介されているコードを元に、対象を投稿日から日付カスタムフィールドの値に置き換えます。ついでに、表示している月にスタイルを当てるために class="current-date" を付与し、開閉ボタンのタグをキーボード操作が可能な button 要素に変更しました。

イベントはカスタム投稿タイプ “event” とし、イベント開催日の日付フィールドの名前は “event_date” としています。

<ul>
<?php
$current_date = '';
$current_year = '';
if ( is_page('monthly') ){
  $current_date = $_GET['date']; // 表示しているページの年月を取得
  $current_year = substr($current_date, 0, 4); // 表示しているページの年を取得
}
?>
<ul class="accordion">
<?php
$y_flg = true; //年の切替フラグ
$f_flg = true; //初回フラグ
$year = idate('Y')+1; //翌年までのイベントを取得
$month = 12; //12月までイベントを取得
$oldest_year = 2023; //一番古いイベントの年
while ( $year >= $oldest_year ) { //一番古いイベント年を指定年が下回るまでループ
  //年見出し出力
  if ( $y_flg == true ){ //年切替フラグが立っていたら
    //指定年のイベント数を取得
    $args = array(
      'post_type' => 'event',
      'meta_query' => array(
        array(
          'key' => 'event_date',
          'value' => $year,
          'compare' => 'LIKE',
        )
      ),
      'post_status' => 'publish',
      'posts_per_page' => -1
    );
    $my_query = new WP_Query( $args );
    $year_archives_num = $my_query->post_count;

    if ( $year_archives_num > 0 ){ //指定年のイベントがあったら閉じた年見出しを出力
      if ( $f_flg == true ){ //初回は閉じタグ不要&開いておく
?>
<li>
<div<?php if ( $year == $current_date ){ echo ' class="current-date"'; } ?>>
<?php
        if ( !is_page('monthly') || (is_page('monthly') && $year == $current_year) ){
          echo '<button aria-label="月リストを開く" aria-expanded="true" class="open"></button>';
        } else {
          echo '<button aria-label="月リストを開く" aria-expanded="false"></button>';
        }
?><a href="<?php echo home_url().'/event/monthly/?date='.$year; ?>"><?php echo $year; ?>年 (<?php echo $year_archives_num; ?>)</a>
</div>
<ul<?php if ( is_page('monthly') && $year != $current_year ){ echo ' style="display: none;"'; } ?>>
<?php
        $f_flg = false; //1度通ったらフラグを倒しておく
      } else { //2回目以降は閉じタグ必要&閉めておく
?>
</ul>
</li>
<li>
<div<?php if ( $year == $current_date ){ echo ' class="current-date"'; } ?>>
<?php
        if ( is_page('monthly') && $year == $current_year ){
          echo '<button aria-label="月リストを開く" aria-expanded="true" class="open"></button>';
        } else {
          echo '<button aria-label="月リストを開く" aria-expanded="false"></button>';
        }
?><a href="<?php echo home_url().'/event/monthly/?date='.$year; ?>"><?php echo $year; ?>年 (<?php echo $year_archives_num; ?>)</a>
</div>
<ul<?php if ( !is_page('monthly') || (is_page('monthly') && $year != $current_year) ){ echo ' style="display: none;"'; } ?>>
<?php
      }
      $y_flg = false; //年見出しが出力されたら年切替フラグを倒しておく
    } else { //該当の年にイベントがなかった場合
      $year--; //1年前へ
      $month = 12; //12月へ
    }
  }
  //月アーカイブ出力
  if ( $y_flg == false ){ //年切替フラグが倒れていたら
    //指定年月のイベント数を取得
    $args = array(
      'post_type' => 'event',
      'meta_query' => array(
        array(
          'key' => 'event_date',
          'value' => $year.sprintf('%02d',$month),
          'compare' => 'LIKE',
        )
      ),
      'post_status' => 'publish',
      'posts_per_page' => -1
    );
    $my_query = new WP_Query( $args );
    $month_archives_num = $my_query->post_count;
    if ( $month_archives_num > 0 ) { //指定年月のイベントがあったらアーカイブリンクを出力
?>
<li><a href="<?php echo home_url().'/event/monthly/?date='.$year.sprintf('%02d',$month); ?>"><?php echo $year; ?>年<?php echo $month; ?>月 (<?php echo $month_archives_num; ?>)</a></li>
<?php
    }
    $month--; //1月前へ
    if ( $month < 1 ){ //0月になってしまったら
      $year--; //1年前へ
      $month = 12; //12月へ
      $y_flg = true; //年切替フラグを立てる
    }
  }
}
?>
</ul>
</li>
</ul>

通常の月別アーカイブと異なる点として、$year$month に代入するは本日の年と月ではなく、本日の翌年と12月としています。通常の投稿日の月別アーカイブは本日までをリスト化すればよいですが、イベント開催日の場合は未来の日付も存在するからです。

また、年毎あるいは月毎の投稿数(イベント数)を取得するために、WP_Query を使い、日付フィールドの値で抽出しています。

あと、それぞれの月のリンク先は、固定ページの URL に date パラメータを追記しています。

jQuery と CSS は省略します。

年月毎のイベントアーカイブページの作成

年月毎のアーカイブページは固定ページを使用します。イベントのメインページの子ページとして mothly というスラッグで作成しましたので、page-monthly.php というテーマファイルを作成し、イベントリストの出力箇所に以下のように記述します。

<?php if ( isset($_GET['date']) ){ $this_month = $_GET['date']; } ?>
<h2><?php echo substr($this_month, 0, 4). '年';
  if ( substr($this_month, 4, 2) ){
    echo sprintf('%d', substr($this_month, 4, 2)). '月';
  } ?>開催のイベント</h2>
<?php
$args = array(
  'post_type' => 'event',
  'meta_query' => array(
    array(
      'key' => 'event_date',
      'value' => $this_month,
      'compare' => 'LIKE',
    )
  ),
  'orderby' => 'meta_value',
  'order' => 'ASC',
  'post_status' => 'publish',
  'paged' => $paged,
  'posts_per_page' => 10
);
$my_query = new WP_Query( $args );
if ( $my_query -> have_posts() ):
  echo '<ul class="archiveList eventList">'. "\n";
  while ( $my_query -> have_posts() ): $my_query -> the_post();
    		
    (イベントリストの出力)
    	
  endwhile;
  echo '</ul>'. "\n";
else:
  echo '<p class="noInfo">該当のイベントはありません。</p>'. "\n";
endif;
wp_reset_postdata();
?>

URL の date パラメータの値と日付フィールドの値の年月または年が一致するイベントを抽出しています。URL パラメータを使うことにより、固定ページを使ってアーカイブページのように振る舞うことができます。

イベント開催日が複数日に渡る場合

上記は単純にイベント開催日が単独の1日の場合ですが、実際には2日間以上に渡ったり、離れた複数の日に開催され、月が跨ることもあります。このような場合、それぞれの月に表示する必要があります。

私の場合、ACF の繰り返しフィールドを使い、開始日と終了日のセットを繰り返し設定できるようにしています。

繰り返しフィールドの名前を event_dates、サブフィールドの開始日を event_start、終了日を event_end とします。

まず、WP_Query を繰り返しフィールドで絞り込むことができるように functions.php に以下を追記します。

function my_posts_where_subfield($where, $query) {
  if ($query->get('subfield_meta_key')) {
    $where = str_replace('meta_key =', 'meta_key LIKE', $where);
  }
  return $where;
}
add_filter('posts_where', 'my_posts_where_subfield', 10, 2);

月別リストの該当箇所を次のように置き換えます。

    //指定年のイベント数を取得
    $args = array(
      'post_type' => 'event',
      'subfield_meta_key' => true,
      'meta_query' => array(
        'relation' => 'OR',
        array(
          'key' => 'event_dates_%_event_start',
          'value' => $year,
          'compare' => 'LIKE',
        ),
        array(
          'key' => 'event_dates_%_event_end',
          'value' => $year,
          'compare' => 'LIKE',
        )
      ),
      'post_status' => 'publish',
      'posts_per_page' => -1
    );
    //指定月のイベント数を取得
    $args = array(
      'post_type' => 'event',
      'subfield_meta_key' => true,
      'meta_query' => array(
        'relation' => 'OR',
        array(
          'key' => 'event_dates_%_event_start',
          'value' => $year.sprintf('%02d',$month),
          'compare' => 'LIKE',
        ),
        array(
          'key' => 'event_dates_%_event_end',
          'value' => $year.sprintf('%02d',$month),
          'compare' => 'LIKE',
        )
      ),
      'post_status' => 'publish',
      'posts_per_page' => -1
    );

アーカイブページ(固定ページ)の該当箇所は以下です。

$args = array(
  'post_type' => 'event',
  'subfield_meta_key' => true,
  'meta_query' => array(
    'relation' => 'OR',
    array(
      'key' => 'event_dates_%_event_start',
      'value' => $this_month,
      'compare' => 'LIKE',
    ),
    array(
      'key' => 'event_dates_%_event_end',
      'value' => $this_month,
      'compare' => 'LIKE',
    )
  ),
  'orderby' => 'meta_value',
  'order' => 'ASC',
  'post_status' => 'publish',
  'paged' => $paged,
  'posts_per_page' => 10
);

以上で理想のカタチで開催年月のアーカイブページを出力することができました。

あとは、アーカイブページの title 属性やパンくずリストが固定ページのタイトルになりますので、表示しいている年月に置き換える処理など、細々とした調整を行なっています。