「このサイトについて」ページに、投稿数の年別累計グラフを表示しています。
このグラフを作るためには次のようなデータが必要です。
$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を選んでみます。