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 コンパクトに全表示形式
過去記事のこのタイプです。dl
dt
で、投稿が有る月はリンク付き、無い月はリンク無しで出力します。
出力される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チャンネルで、ご相談やご質問を募集しています。動画のコメントやお問い合わせページからお気軽にご相談をお寄せください。