WordPress: 投稿数を年別に集計する


「このサイトについて」ページに、投稿数の年別累計グラフを表示しています。

このグラフを作るためには次のようなデータが必要です。

$years = [ 2005, 2006, ... ] //横軸(年)
$counts = [ 100, 200, ... ] // 縦軸(累積投稿数)

取得方法をいくつか検討しました。

1. wp_get_archives()を使う

wp_get_archives()を使うと、年別のアーカイブ(記事一覧)ページへのリンクと投稿数を得ることができます。ただし問題が2つ。

まず、得られるのが次のようなHTML文字列であるということ。

<li><a href=\'https://listfreak.com/date/2006\'>2006</a> (114)</li>

そのまま表示するには都合がよいのですが、いま必要なのはグラフの横軸に使う投稿があった年の配列と、縦軸に使う年ごとの投稿数の累積値の配列です。したがって、生成されたHTMLから年と投稿数を切り出す必要があります。

次に、カテゴリ別に投稿数を絞り込むパラメータが無いこと。当サイトはpostという投稿タイプの中にlistカテゴリのリスト投稿とblogカテゴリのブログ記事があり、集計したいのはlistカテゴリの記事だけです。したがって、何らかの方法でlistカテゴリの記事だけを集計しなければなりません。

上記を踏まえ、次のようなコードを書きました。

add_filter( 'getarchives_join', 'lf_getarchives_join', 10, 2 );

$out = wp_get_archives( [
	'type'            => 'yearly',
	'show_post_count' => true,
	'echo'            => false,
	'order'           => 'ASC',
] );

remove_filter( 'getarchives_join', 'lf_getarchives_join' );

// 生成されたリストから年と投稿数を切り出す
preg_match_all(
    '|\>(\d+)\</a.+\((\d+)\)\</li|',
    $archives,
    $matches
);

// 年を数値に変換
$years = array_map( 'intval', $matches[1] );

// 年別投稿数を数値に変換して累積値を取得
$counts = [];
foreach ( $matches[2] as $i => $match ) {
    $counts[] += (int)$match + ( $counts[$i-1] ?? 0 );
}

function lf_getarchives_join( $sql_join, $parsed_args ) {
	return "
	JOIN wp_term_relationships AS tr ON wp_posts.ID = tr.object_id 
	JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'category'
	JOIN wp_terms AS t ON tt.term_id = t.term_id AND t.name='list'
	";
}

そして $years を横軸に、$counts を縦軸にしたグラフを描画させます。

2. SQLを直接発行する

JOIN句をずらずら書かなければいけないのなら、標準関数を使わずSQLを発行するのも手です。

$query = "SELECT YEAR(post_date) AS year, COUNT(post_date) AS count
FROM wp_posts AS p
JOIN wp_term_relationships AS tr ON p.ID = tr.object_id 
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'category'
JOIN wp_terms AS t ON tt.term_id = t.term_id AND t.name='list'
WHERE p.post_status='publish' AND p.post_type='post'
GROUP BY YEAR(post_date)";
$publists = $wpdb->get_results( $query );

// グラフの横軸(年)と縦軸(累積投稿数)データを取得
foreach ( $publists as $i => &$publist ) {

    // 前年の投稿数(初年度の前年は0)を加算
    $publist->count += ( $publists[$i-1]->count ?? 0 );

    $years[]  = $publist->year;
    $counts[] = $publist->count;
}

3. 標準関数を組み合わせる

最初の投稿と最新の投稿の年を取得したうえで、年ごとの投稿数を取得していきます。wp_get_archives()の存在を知らなかった頃に作りました。

// 全クエリ共通
$query_base = [
    'post_type'      => 'post',
    'post_status'    => 'publish',
    'category_name'  => 'list',
];

foreach( [ 'ASC', 'DESC' ] as $order ) {
    $query = new WP_Query( $query_base + [
        'posts_per_page' => 1,
        'orderby'        => 'date',
        'order'          => $order,
    ] );

    // 投稿日の最初の4文字を数値化して投稿年を取得
    $date = $query->posts[0]->post_date;
    $year[$order] = intval( substr( $date, 0, 4 ) );
}

// 投稿数の年別累計値を取得
$years = $counts = [];
for( $y = $year['ASC']; $y <= $year['DESC']; $y++ ) {
    $query = new WP_Query( $query_base + [
        'posts_per_page' => 1, // ヒット件数(found_posts)だけわかればよいので実データは最小
        'fields'         => 'ids', // ID のみ
        'year'           => $y,
    ] );

    $years[]  = $y;
    $counts[] = ( $counts[$y-1] ?? 0 ) + $query->found_posts;
}

どれを採用するか

SQL文の発行回数が少ない順はおそらく 2、1、3。ただし2はオブジェクトキャッシュを実装する必要があります。

変更に強そうなのは 3、2、1 でしょうか。2本来隠蔽されているべきDBアクセスを直書きしています。加えて1は標準関数の吐き出すHTMLの構造が変われば正規表現も変えねばなりません。

とりあえず1を選んでみます。

Posted by

on

in category