WordPressで指定カテゴリのアーカイブリストを作成する

WordPressで指定カテゴリのアーカイブリストを作成する

2016年(5年前!)に書いた、月別アーカイブリンクをカスタマイズする記事に、カテゴリで絞り込めないかというご質問を受けました! 調べてみたらいけそうな感じがしたので書いてみました~! 久しぶりにWordPressの記事です!!


はじめに

今回は、以下の記事を参考にして書かせていただいてます。リンクに?cat_slug=のクエリを渡すという考え方が「なるほど~!!」と感激しました! ありがとうございます!

もはや私の書くことなど…、とも思うのですが、過去記事に質問をいただいた経緯もあるので、カテゴリで絞った件数をSQLで取得してみたり、アーカイブページの絞り込みをpre_get_postsでやってみたりなど、私なりの「別解」のような気持ちで書かせていただきました。

完成図

過去に月別アーカイブに関する記事を2本ほど書いたことがあるので、その2つの出力方法について書いてみます。この記事ではPHPと出力されるHTMLのみ紹介しますので、CSSや装飾に関わる部分は過去記事(後述します)をご覧くださいね。

アーカイブページを指定カテゴリで絞り込む

まずはここから。

アーカイブページが表示されていて、なおかつ?cat_slug=XXXのクエリ(XXXはカテゴリーのスラッグ)がURLに付加されていた場合、結果をそのカテゴリーで絞り込むというテコ入れをします。functions.phpに以下を記述します。

//アーカイブをカテゴリで絞り込み
function CatArchives( $query ) {
	if ( is_admin() || ! $query->is_main_query() )
		return;

	if ( $query->is_archive() || $query->is_home() ) {
		$cat_slug = (string)filter_input(INPUT_GET, 'cat_slug'); //カテゴリスラッグを取得
		$cat_id = get_category_by_slug( $cat_slug ) -> cat_ID; //スラッグからカテゴリIDを取得
		if ( $cat_id != 0 ) { //カテゴリIDがあれば
			$query->set( 'cat', $cat_id ); //指定カテゴリで絞り込む
			$query->set( 'orderby', 'date' ); //日付降順で並び替え
		}
		return;
	}
}
add_action( 'pre_get_posts', 'CatArchives' );

この記述をしたうえで、以下のようにクエリを付加(XXXはカテゴリーのスラッグ)したURLを叩いて指定のカテゴリに絞り込めればOKです。

  • https://sample.com/?cat_slug=XXX
  • https://sample.com/2021/?cat_slug=XXX
  • https://sample.com/2021/01/?cat_slug=XXX

投稿数を取得する関数を書く

指定したカテゴリに対して、年の投稿数、年月の投稿数、一番古い記事の投稿年を取得する関数を書きます。functions.phpに以下を記述します。

// カテゴリ・年で絞った投稿数を取得
function get_year_cat_archives_num( $cat_slug, $year ) {
	$cat_id = get_category_by_slug( $cat_slug ) -> cat_ID;
	global $wpdb;
	$cnt = $wpdb->get_var( $wpdb->prepare("
		SELECT count(DISTINCT ps.ID)
		FROM $wpdb->posts AS ps
		INNER JOIN $wpdb->term_relationships AS tm
		ON ps.ID = tm.object_id
		WHERE ps.post_status = 'publish'
		AND ps.post_type = 'post'
		AND tm.term_taxonomy_id = %d
		AND DATE_FORMAT(ps.post_date, '%Y') = '%s'
	" , $cat_id, $year ) );
	return $cnt;
}
// カテゴリ・年・月で絞った投稿数を取得
function get_month_cat_archives_num( $cat_slug, $year, $month ) {
	$cat_id = get_category_by_slug( $cat_slug ) -> cat_ID;
	global $wpdb;
	$cnt = $wpdb->get_var( $wpdb->prepare("
		SELECT count(DISTINCT ps.ID)
		FROM $wpdb->posts AS ps
		INNER JOIN $wpdb->term_relationships AS tm
		ON ps.ID = tm.object_id
		WHERE ps.post_status = 'publish'
		AND ps.post_type = 'post'
		AND tm.term_taxonomy_id = %d
		AND DATE_FORMAT(ps.post_date, '%Y%m') = '%s'
	" , $cat_id, $year.str_pad($month, 2, 0, STR_PAD_LEFT) ) );
	return $cnt;
}
// 指定カテゴリの一番古い記事の投稿年を取得
function get_cat_oldest_year( $cat_slug ) {
	$catId = get_category_by_slug( $cat_slug ) -> cat_ID;
	global $wpdb;
	$oldest_date = $wpdb->get_var( $wpdb->prepare("
		SELECT ps.post_date
		FROM $wpdb->posts AS ps
		INNER JOIN $wpdb->term_relationships AS tm
		ON ps.ID = tm.object_id
		WHERE ps.post_status = 'publish'
		AND ps.post_type = 'post'
		AND tm.term_taxonomy_id = %d
		ORDER BY ps.post_date ASC LIMIT 1
	" , $cat_id ) );
	return  idate('Y', strtotime($oldest_date) );
}

データベースにSQL文を投げて直接ぶっこ抜くという荒業でご紹介しておりますが、いろんなアプローチあるので、こんな方法もあるんだ~程度でお願いしますw

このあたりの詳しいことはむかしまとめたことがあります。

出力その1 開閉リスト形式

過去記事のこのタイプです。ulリストで年で折りたたみます。

出力されるHTML

<ul class="accordion">
	<li>
		<p><span class="acv_open"></span>YYYY年 (n)</p>
		<ul>
			<li><a href="https://sample.com/YYYY/MM/?cat_slug=XXX">YYYY年MM月</a> (n)</li>
			<li><a href="https://sample.com/YYYY/MM/?cat_slug=XXX">YYYY年MM月</a> (n)</li>
			<!-- 略 -->
			<li><a href="https://sample.com/YYYY/MM/?cat_slug=XXX">YYYY年MM月</a> (n)</li>
		</ul>
	</li>
	<li>
		<p><span></span>YYYY年 (n)</p>
		<ul class="hide">
			<li><a href="https://sample.com/YYYY/MM/?cat_slug=XXX">YYYY年MM月</a> (n)</li>
			<li><a href="https://sample.com/YYYY/MM/?cat_slug=XXX">YYYY年MM月</a> (n)</li>
			<li><a href="https://sample.com/YYYY/MM/?cat_slug=XXX">YYYY年MM月</a> (n)</li>
			<!-- 略 -->
			<li><a href="https://sample.com/YYYY/MM/?cat_slug=XXX">YYYY年MM月</a> (n)</li>
		</ul>
	</li>
	<!-- 一番古い投稿日まで続く -->
</ul>

投稿が存在する場合のみ、新しい年月順に並べます。アイコンや開閉の動きをつけるためにspanやclassなど付いています。これらのCSSやjQueryは過去記事を参照ください。ていうか2021年の今、jQueryってもう時代遅れだったりしますか!? ぜひイマドキの書き方にカスタマイズしてくださいね…。

任意の場所に書くPHP

<ul class="accordion">
<?php
$cat_slug = 'XXX'; //カテゴリをスラッグで指定
$y_flg = true; //年の切替フラグ
$f_flg = true; //初回フラグ
$year = idate('Y'); //本日の年
$month = idate('m'); //本日の月
$cat_oldest_year = get_cat_oldest_year( $cat_slug ); //一番古い投稿日の年
while ( $year >= $cat_oldest_year ) { //一番古い投稿年を指定年が下回るまでループ
  //年見出し出力
  if ( $y_flg == true ){ //年切替フラグが立っていたら
    $year_cat_archives_num = get_year_cat_archives_num( $cat_slug, $year ); //指定年の投稿数を取得
    if ( $year_cat_archives_num > 0 ){ //指定年の投稿があったら閉じた年見出しを出力
      if ( $f_flg == true ){ //初回は閉じタグ不要&開いておく
?>
<li>
<p><span class="acv_open"></span><?php echo $year; ?>年 (<?php echo $year_cat_archives_num; ?>)</p>
<ul>
<?php
        $f_flg = false; //1度通ったらフラグを倒しておく
      } else { //2回目以降は閉じタグ必要&閉めておく
?>
</ul>
</li>
<li>
<p><span></span><?php echo $year; ?>年 (<?php echo $year_cat_archives_num; ?>)</p>
<ul class="hide">
<?php
      }
      $y_flg = false; //年見出しが出力されたら年切替フラグを倒しておく
    } else { //該当の年に投稿がなかった場合
      $year--; //1年前へ
      $month = 12; //12月へ
    }
  }
  //月アーカイブ出力
  if ( $y_flg == false ){ //年切替フラグが倒れていたら
    $month_cat_archives_num = get_month_cat_archives_num($cat_slug, $year, $month); //指定年月の投稿数を取得
    if ( $month_cat_archives_num > 0 ) { //指定年月の投稿があったらアーカイブリンクを出力
?>
<li><a href="<?php echo home_url('/').$year.'/'.str_pad($month, 2, 0, STR_PAD_LEFT).'/?cat_slug='.$cat_slug; ?>"><?php echo $year."年".$month."月"; ?></a> (<?php echo $month_cat_archives_num; ?>)</li>
<?php
    }
    $month--; //1月前へ
    if ( $month < 1 ){ //0月になってしまったら
      $year--; //1年前へ
      $month = 12; //12月へ
      $y_flg = true; //年切替フラグを立てる
    }
  }
}
?>
</ul>
</li>
</ul>

こちらがアーカイブリストを出したい場所に書くPHPです。3行目のスラッグだけ指定してください。

出力その2 コンパクトに全表示形式

過去記事のこのタイプです。dldtで、投稿が有る月はリンク付き、無い月はリンク無しで出力します。

出力されるHTML

<div id="ArchivesArea">
<dl>
	<dt><a href="https://sample.com/YYYY/?cat_slug=XXX" title="n件の投稿">YYYY</a>:</dt>
		<dd><a href="https://sample.com/YYYY/01/?cat_slug=XXX" title="n件の投稿">01</a></dd>
		<dd>02</dd>
		<dd><a href="https://sample.com/YYYY/03/?cat_slug=XXX" title="n件の投稿">03</a></dd>
		<dd>04</dd>
		<dd><a href="https://sample.com/YYYY/05/?cat_slug=XXX" title="n件の投稿">05</a></dd>
		<dd><a href="https://sample.com/YYYY/06/?cat_slug=XXX" title="n件の投稿">06</a></dd>
		<dd><a href="https://sample.com/YYYY/07/?cat_slug=XXX" title="n件の投稿">07</a></dd>
		<dd><a href="https://sample.com/YYYY/08/?cat_slug=XXX" title="n件の投稿">08</a></dd>
		<dd><a href="https://sample.com/YYYY/09/?cat_slug=XXX" title="n件の投稿">09</a></dd>
		<dd>10</dd>
		<dd>11</dd>
		<dd>12</dd>
	<dt><a href="https://sample.com/YYYY/?cat_slug=XXX" title="n件の投稿">YYYY</a>:</dt>
		<dd>01</dd>
		<dd>02</dd>
		<dd>03</dd>
		<dd>04</dd>
		<dd>05</dd>
		<dd>06</dd>
		<dd><a href="https://sample.com/YYYY/07/?cat_slug=XXX" title="n件の投稿">07</a></dd>
		<dd><a href="https://sample.com/YYYY/08/?cat_slug=XXX" title="n件の投稿">08</a></dd>
		<dd>09</dd>
		<dd>10</dd>
		<dd><a href="https://sample.com/YYYY/11/?cat_slug=XXX" title="n件の投稿">11</a></dd>
		<dd><a href="https://sample.com/YYYY/12/?cat_slug=XXX" title="n件の投稿">12</a></dd>
	<!-- 一番古い投稿年まで続く -->
</dl>
</div><!-- /#ArchivesArea -->

現在の年から一番古い投稿の年まで出力します。投稿がないところはただのテキストです。CSSは過去記事を参照ください。

任意の場所に書くPHP

<div id="ArchivesArea">
<dl>
<?php
$cat_slug = 'XXX'; //カテゴリをスラッグで指定
$flg = true; //年の切替フラグ
$acv_str = ""; //出力文字列
$year = idate('Y'); //本日の年
$month = 1; //1月
$cat_oldest_year = get_cat_oldest_year( $cat_slug ); //一番古い投稿日の年
while ( $year >= $cat_oldest_year ) { //一番古い投稿年を指定年が下回るまでループ
  //年見出し出力
  if ( $flg == true ){ //年切替フラグが立っていたら
    $year_cat_archives_num = get_year_cat_archives_num( $cat_slug, $year ); //指定年の投稿数を取得
    if ( $year_cat_archives_num > 0 ) { //投稿があったらアーカイブリンクを生成
      $acv_str .= "\t".'<dt><a href="'.home_url('/').$year.'/?cat_slug='.$cat_slug.'" title="'.$year_cat_archives_num.'件の投稿">'.$year."</a>:</dt>\n";
    } else { //指定月の投稿がなかったら文字列だけ
      $acv_str .= "\t<dt>".$year.":</dt>\n";
    }
    $flg = false; //年見出しが出現したら年切替フラグを倒しておく
  }
  //月アーカイブ出力
  if ( $flg == false ){ //年切替フラグが倒れていたら
    $month_cat_archives_num = get_month_cat_archives_num( $cat_slug, $year, $month ); //指定年月の投稿数を取得
    if ( $month_cat_archives_num > 0 ) { //指定月の投稿があったらアーカイブリンクを生成
		$acv_str .= "\t\t".'<dd><a href="'.home_url('/').$year.'/'.str_pad($month, 2, 0, STR_PAD_LEFT).'/?cat_slug='.$cat_slug.'" title="'.$month_cat_archives_num.'件の投稿">'.str_pad($month, 2, 0, STR_PAD_LEFT)."</a></dd>\n";
    } else { //指定月の投稿がなかったら文字列だけ
      $acv_str .= "\t\t<dd>".str_pad($month, 2, 0, STR_PAD_LEFT)."</dd>\n";
    }
    $month++; //1月進む
    if ( $month > 12 ){ //13月になってしまったら
      $year--; //1年前へ
      $month = 1; //1月へ
      $flg = true; //年切替フラグを立てる
    }
  }
}
echo $acv_str; //出力
?>
</dl>
</div><!-- /#ArchivesArea -->

こちらがアーカイブリストを出したい場所に書くPHPです。4行目のスラッグだけ指定してください。

公開日:2021/07/09

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

コメントは承認制ですので、反映までしばらくお待ち下さい。(稀にスパムの誤判定にて届かないこともあるようですので、必要な際はお問い合わせからお願い致します。)

YouTubeでQ&Aコンテンツを企画しています

運営しているYouTubeチャンネルで、ご相談やご質問を募集しています。動画のコメントやお問い合わせページからお気軽にご相談をお寄せください。