はじめに
ブログを運営していると、過去の記事を年別・年月別に整理して、サイドバーや固定ページに見やすく表示したい場面があります。
通常、Cocoonの navi_list ショートコードを使ってアーカイブ一覧をナビカード形式で表示するには、外観→メニューから年別・月別のリンクを手動で登録する必要があります。
しかし、年や月が増えるたびにメニューを更新する手間が発生してしまいます。
そこで、Cocoonの navi_list の仕組みを活用し、投稿が存在する年や年月を自動取得して、ナビカード形式で表示する仕組みを追加します。
年別アーカイブは各年を独立したカードとして表示し、年月別アーカイブは年を親カード、月を子カードとして階層構造で表示します。
また、最新投稿のアイキャッチ画像をカードに使用し、現在表示しているアーカイブを強調表示できるようにします。投稿が増えても自動で反映されるため、アーカイブのカードを手動で管理する必要はありません。
この記事では、この方法について説明します。
完成イメージ

使用方法
以下にショートコードを示します。
- 年別アーカイブ
[navi_list source="year"]
- 年月別アーカイブ
[navi_list source="month"]
実装方法
以下のコードをfunctions.phpに追加します。
- ラベル
- ラベルPHPを追加(年別アーカイブ一覧)
以下のコードをfunctions.phpに追加します。
add_filter('pre_do_shortcode_tag', function($output, $tag, $attr, $m) { if ($tag !== 'navi_list') { return $output; } // source=year以外は処理対象外 if (!is_array($attr) || ($attr['source'] ?? '') !== 'year') { return $output; } global $wpdb; // 投稿が存在する年を新しい順で取得 $years = $wpdb->get_col($wpdb->prepare( "SELECT DISTINCT YEAR(post_date) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s AND post_date <= %s ORDER BY YEAR(post_date) DESC", 'post', 'publish', current_time('mysql') )); // 年別アーカイブが存在しない場合は終了 if (empty($years)) { return ''; } // カードHTMLを初期化 $cards = ''; // 年ごとにカードを生成 foreach ($years as $year) { // その年の最新投稿を取得 $posts = get_posts([ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 1, 'orderby' => 'date', 'order' => 'DESC', 'date_query' => [ [ 'year' => (int) $year, ], ], 'ignore_sticky_posts' => true, ]); // 投稿がない場合は処理対象外 if (empty($posts)) { continue; } $post = $posts[0]; // 年別アーカイブを擬似メニュー項目形式に変換 $menu = (object) [ 'object' => 'post', 'object_id' => $post->ID, 'url' => get_year_link((int) $year), 'title' => $year . '年', 'description' => '', 'classes' => [], 'target' => '', 'attr_title' => get_the_post_thumbnail_url($post->ID, 'full') ?: '', ]; // カード表示用の属性を設定 $card_atts = [ 'prefix' => WIDGET_NAVI_ENTRY_CARD_PREFIX, 'url' => $menu->url, 'title' => escape_shortcodes($menu->title), 'snippet' => '', 'image_attributes' => get_navi_card_image_attributes($menu, 0), 'ribbon_no' => null, 'type' => 0, 'classes' => [], 'object' => 'post', 'object_id' => $post->ID, 'horizontal' => 0, 'target' => '', ]; // Cocoon標準のナビカードを取得 $card = get_widget_entry_card_link_tag($card_atts); // 階層情報とカレント情報をカードリンクに追加 $card = str_replace( '<a ', '<a data-menu-id="' . esc_attr($year) . '" data-menu-parent="0" data-menu-current="' . (is_year() && (int) get_query_var('year') === (int) $year ? '1' : '0') . '" ', $card ); // カードを追加 $cards .= $card; } // カードがない場合は終了 if ($cards === '') { return ''; } // Cocoon標準のナビカード外枠を設定 $wrap_atts = [ 'tag' => $cards, 'type' => 0, 'bold' => 1, 'arrow' => 0, 'class' => '', 'horizontal' => 0, ]; // Cocoon標準のナビカード外枠を取得 $html = get_navi_card_wrap_tag($wrap_atts); // 階層用クラスを追加 return str_replace( 'navi-entry-cards', 'navi-entry-cards my-navi-hierarchy', $html ); }, 10, 4); - ラベルPHPを追加(年月アーカイブ一覧)
以下のコードをfunctions.phpに追加します。
add_filter('pre_do_shortcode_tag', function($output, $tag, $attr, $m) { if ($tag !== 'navi_list') { return $output; } // source=month以外は処理対象外 if (!is_array($attr) || ($attr['source'] ?? '') !== 'month') { return $output; } global $wpdb; // 投稿が存在する年月を取得 $archives = $wpdb->get_results($wpdb->prepare( "SELECT YEAR(post_date) AS year, MONTH(post_date) AS month FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s AND post_date <= %s GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY year DESC, month DESC", 'post', 'publish', current_time('mysql') )); // アーカイブが存在しない場合は終了 if (empty($archives)) { return ''; } // 年ごとの月を整理 $months_by_year = []; foreach ($archives as $archive) { $months_by_year[$archive->year][] = $archive->month; } // カードHTMLを初期化 $cards = ''; // 年ごとに親カードを生成 foreach ($months_by_year as $year => $months) { // その年の最新投稿を取得 $year_posts = get_posts([ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 1, 'orderby' => 'date', 'order' => 'DESC', 'date_query' => [ [ 'year' => (int) $year, ], ], 'ignore_sticky_posts' => true, ]); // 投稿がない場合は処理対象外 if (empty($year_posts)) { continue; } $year_post = $year_posts[0]; // 年別アーカイブを擬似メニュー項目形式に変換 $year_menu = (object) [ 'object' => 'post', 'object_id' => $year_post->ID, 'url' => get_year_link((int) $year), 'title' => $year . '年', 'description' => '', 'classes' => [], 'target' => '', 'attr_title' => get_the_post_thumbnail_url($year_post->ID, 'full') ?: '', ]; // 年別カードの表示用属性を設定 $year_card_atts = [ 'prefix' => WIDGET_NAVI_ENTRY_CARD_PREFIX, 'url' => $year_menu->url, 'title' => escape_shortcodes($year_menu->title), 'snippet' => '', 'image_attributes' => get_navi_card_image_attributes($year_menu, 0), 'ribbon_no' => null, 'type' => 0, 'classes' => [], 'object' => 'post', 'object_id' => $year_post->ID, 'horizontal' => 0, 'target' => '', ]; // Cocoon標準のナビカードを取得 $year_card = get_widget_entry_card_link_tag($year_card_atts); // 階層情報とカレント情報をカードリンクに追加 $year_card = str_replace( '<a ', '<a data-menu-id="' . esc_attr($year) . '" data-menu-parent="0" data-menu-current="' . (is_year() && (int) get_query_var('year') === (int) $year ? '1' : '0') . '" ', $year_card ); // 年別カードを追加 $cards .= $year_card; // 月ごとに子カードを生成 foreach ($months as $month) { // その月の最新投稿を取得 $month_posts = get_posts([ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 1, 'orderby' => 'date', 'order' => 'DESC', 'date_query' => [ [ 'year' => (int) $year, 'month' => (int) $month, ], ], 'ignore_sticky_posts' => true, ]); // 投稿がない場合は処理対象外 if (empty($month_posts)) { continue; } $month_post = $month_posts[0]; // 月別アーカイブを擬似メニュー項目形式に変換 $month_menu = (object) [ 'object' => 'post', 'object_id' => $month_post->ID, 'url' => get_month_link((int) $year, (int) $month), 'title' => $year . '年' . sprintf('%02d', $month) . '月', 'description' => '', 'classes' => [], 'target' => '', 'attr_title' => get_the_post_thumbnail_url($month_post->ID, 'full') ?: '', ]; // 月別カードの表示用属性を設定 $month_card_atts = [ 'prefix' => WIDGET_NAVI_ENTRY_CARD_PREFIX, 'url' => $month_menu->url, 'title' => escape_shortcodes($month_menu->title), 'snippet' => '', 'image_attributes' => get_navi_card_image_attributes($month_menu, 0), 'ribbon_no' => null, 'type' => 0, 'classes' => [], 'object' => 'post', 'object_id' => $month_post->ID, 'horizontal' => 0, 'target' => '', ]; // Cocoon標準のナビカードを取得 $month_card = get_widget_entry_card_link_tag($month_card_atts); // 階層情報とカレント情報をカードリンクに追加 $month_card = str_replace( '<a ', '<a data-menu-id="' . esc_attr($year . '-' . $month) . '" data-menu-parent="' . esc_attr($year) . '" data-menu-current="' . (is_month() && (int) get_query_var('year') === (int) $year && (int) get_query_var('monthnum') === (int) $month ? '1' : '0') . '" ', $month_card ); // 月別カードを追加 $cards .= $month_card; } } // カードがない場合は終了 if ($cards === '') { return ''; } // Cocoon標準のナビカード外枠を設定 $wrap_atts = [ 'tag' => $cards, 'type' => 0, 'bold' => 1, 'arrow' => 0, 'class' => '', 'horizontal' => 0, ]; // Cocoon標準のナビカード外枠を取得 $html = get_navi_card_wrap_tag($wrap_atts); // 階層用クラスを追加 return str_replace( 'navi-entry-cards', 'navi-entry-cards my-navi-hierarchy', $html ); }, 10, 4);
さいごに
今回紹介した方法を使えば、投稿が存在する年や年月を自動取得し、Cocoonのナビカード形式で階層表示できます。
年別アーカイブは各年を独立したカードとして表示し、年月別アーカイブは年を親カード、月を子カードとして表示できます。また、現在表示しているアーカイブを強調表示することも可能です。
カードには各年・月の最新投稿のアイキャッチ画像を使用でき、投稿が追加されても自動的に反映されます。
既存の navi_list ショートコードでは実現できない年別・年月別アーカイブの階層表示を、ナビカード形式で実現できます。


