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行目のスラッグだけ指定してください。






コメントは承認制ですので、反映までしばらくお待ち下さい。(稀にスパムの誤判定にて届かないこともあるようですので、必要な際はお問い合わせからお願い致します。)
YouTubeでQ&Aコンテンツを企画しています
運営しているYouTubeチャンネルで、ご相談やご質問を募集しています。動画のコメントやお問い合わせページからお気軽にご相談をお寄せください。