カテゴリー毎の日付別アーカイブを表示する

WordPress ではサイドバー辺りに年別/月別/日付別(以下、日付別)アーカイブの一覧を表示させるときによく wp_get_archives() を使いますが、これはブログ全体のアーカイブを出力するものです。例えば、カテゴリーの一覧で各々のカテゴリに属するアーカイブを表示したい、という要求はよくあると思われますが、WordPress には特定のカテゴリーの日付別アーカイブ一覧を一言で表示する機能がありません。
そんなようなプラグインはありそうですが探すのが大変そうで心が折れます。微妙にやりたいことと違うとか、バグやらアップデートやら自分の管理下に無いのもアレですので、自力で何とかするレベルを超えていないと見切ったら、チャッと作ってしまいましょう。
基本機能に無くても大体はなんとかなる WordPress+wpxtreme ですので、どんと任せてください。

相変わらずのフォーラムネタです。同じようなトピックが散見されますので、ここらで決定打を放ちましょう。もう、カテゴリー別×日付別アーカイブ問題で悩まなくて OK! 78364!
幸いなことに、デフォルトパーマリンクを使用して http://example.com/?m=2010&cat=123 のようにカテゴリーと日付を指定さえすれば該当する記事達を取得することはできるので、これを利用することにします。

仕様を決める

  1. 普通のカテゴリーリンクをクリックする。
  2. そのカテゴリーのアーカイブが表示される。
    当該カテゴリーを含む記事の年/月/日(以下、日付)アーカイブリストを作成します。これはクエリを工夫すれば良さそうです。
  3. そのアーカイブのパーマリンクは http://example.com/?m=2010&cat=123 の形式になっている。
    2.で取得したアーカイブのパーマリンクは ?m=2010 止まりなので、カテゴリの指定を追加します。
  4. そのリンクをクリックすると、選択したカテゴリーかつ選択した日付の記事一覧を表示する。
    これはWordPress の基本機能。

wp_get_archives はフックできるか?

ソースを見ると、アーカイブ取得のクエリに getarchives_where, getarchives_join のフィルタが使われています。なんてラッキー!もうデキたも同然です。これらのフィルタでは、実行しようとしているクエリ(SQL文)に対して独自の WHERE句, JOIN句 を追加することができます。WHERE は条件の指定、JOIN はテーブルの結合。カテゴリーを指定する条件を加えれば OK ですね。
wp-includes/general-template.php 800行目〜

$where = apply_filters('getarchives_where', "WHERE post_type = 'post' AND post_status = 'publish'", $r );
$join = apply_filters('getarchives_join', "", $r);

指定カテゴリーを含む記事の日付別アーカイブを取得する

function my_getarchives_join($join, $r){
  global $wpdb;
  return
    " LEFT JOIN $wpdb->term_relationships as r ON $wpdb->posts.ID = r.object_ID
      LEFT JOIN $wpdb->term_taxonomy as t ON r.term_taxonomy_id = t.term_taxonomy_id
      LEFT JOIN $wpdb->terms as terms ON t.term_id = terms.term_id";
}
add_filter('getarchives_join', 'my_getarchives_join', 10, 2);

まず、別々になっている投稿のテーブルとカテゴリーのテーブルを結合します。これで投稿を絞るときにカテゴリーを条件として使用できるようになります。

function my_getarchives_where($where, $r){
  return $where . 
    " AND t.taxonomy = 'category' AND terms.term_id = ".get_query_var('cat');
}
add_filter('getarchives_where', 'my_getarchives_where', 10, 2);

カテゴリーID を指定します。get_query_var('cat') で、カテゴリーページを表示している時にその ID を取得することができます。ここまでを category.php に書けば、仕様の 2.が完成です。

日付別アーカイブのリンクをカテゴリ別×日付別アーカイブのリンクにする

ここでは「年別アーカイブ」について実現することにします。wp_get_archives の内部を見ると get_year_link で年別アーカイブのリンクを取得しています。
wp-includes/general-template.php 841行目
$url = get_year_link($arcresult->year);

get_year_link の内部では、リンク作成後に year_link フィルタを呼んでいます。
wp-includes/linkl-template.php 295行目
return apply_filters('year_link', trailingslashit(get_option('home')) . '?m=' . $year, $year);

おー これは楽チンですね、後ろに &cat=123などと追加すれば OK です。

function my_year_link($url, $year){
  global $wp_rewrite;
  $yearlink = $wp_rewrite->get_year_permastruct();
  if(empty($yearlink))
    return $url . '&cat=' . get_query_var('cat');
  
  return $url;      
}
add_filter('year_link', 'my_year_link', 10, 2);

カテゴリページでのみ有効にする

サイドバーでは通常のブログ全体のアーカイブを表示するかもしれませんので、これらの変更は category.php でのみ有効にします。あとは、仕様2.のクリックではアーカイブ表示、仕様4.の時は記事一覧表示の処理を加えます。仕様3.のリンクをクリックすると、カテゴリーが指定されているため category.php が実行されます。なので、これら一連の処理は category.php 内で完結できます。

<?php
function my_year_link($url, $year){
  global $wp_rewrite;
  $yearlink = $wp_rewrite->get_year_permastruct();
  if(empty($yearlink))
    return $url . '&cat=' . get_query_var('cat');
  
  return $url;      
}
function my_getarchives_where($where, $r){
  return $where . 
    " AND t.taxonomy = 'category' AND terms.term_id = ".get_query_var('cat');
}
function my_getarchives_join($join, $r){
  global $wpdb;
  return
    " LEFT JOIN $wpdb->term_relationships as r ON $wpdb->posts.ID = r.object_ID
      LEFT JOIN $wpdb->term_taxonomy as t ON r.term_taxonomy_id = t.term_taxonomy_id
      LEFT JOIN $wpdb->terms as terms ON t.term_id = terms.term_id";
}
add_filter('year_link', 'my_year_link', 10, 2);
add_filter('getarchives_where', 'my_getarchives_where', 10, 2);
add_filter('getarchives_join', 'my_getarchives_join', 10, 2);
get_header(); ?>
<body>
  <ul>
    <?php 
      if(is_year()) :  // 日付があれば、日付×カテゴリなので当該記事一覧を表示する。
        while(have_posts()) :
	  the_post(); 
	?><li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li><?php	
        endwhile;
      else: // 通常のカテゴリリンクの場合は、アーカイブを表示する。
        wp_get_archives('type=yearly');
      endif; 
      ?>
  </ul>
</body>
<?php get_footer(); 
remove_filter('year_link', 'my_year_link', 10, 2);
remove_filter('getarchives_where', 'my_getarchives_where', 10, 2);
remove_filter('getarchives_join', 'my_getarchives_join', 10, 2);
?>

一部のカテゴリーのみ上記アーカイブ表示をする

例えば、カテゴリーID = 12, 34 のときのみ、このアーカイブ表示をして、他のカテゴリーではまた別の表示にする場合。

  1. archives-by-category.php(例)を作成して上記コードを記述。
  2. your-category.php(例)を別途お好みな内容で作成します。
  3. category.php に以下を記述。
    <?php
    $post = $wp_query->post;
    if(in_category('12') || in_category('34')){
     include(TEMPLATEPATH . '/archives-by-category.php');
    }else{
     include(TEMPLATEPATH . '/your-category.php');
    }
    ?>

wp_get_archives 実行時に getarchives_where, getarchives_join, year_link フィルタをフックしてカテゴリーを設定すれば、他のシチュエーションでも実現可能です。single.php で表示記事と同じカテゴリの年月アーカイブを表示、とか。チャレンジしてみてくださいー。

参照:フォーラム
2つの特定カテゴリに年別アーカイブを表示させたい
動作確認バージョン
  • WordPress 2.9.1

6 Comments

  • 指定カテゴリーを含む記事の日付別アーカイブ

  • カテゴリー毎の日付別アーカイブを表示する  |  wpxtreme http://t.co/mJqCmth

  • wordpressgoat wordpressgoat

    カテゴリー毎の日付別アーカイブを表示する  |  wpxtreme http://t.co/eKmNWHDA #wordpress #wp

  • 古い記事だけどこちらも独自でやる方法