
WordPressで人気記事ランキングを表示するには、専用プラグインを使う方法が一般的ですが、「自由度が低い」「postmetaが肥大化する」などの課題もあります。この記事では、独自テーブルを使って週間・月間の閲覧数を集計し、テンプレート内で表示するランキング機能を自作する方法を、具体的なコード例とともに解説します。
目次
自作する理由と設計の前提
プラグインを使わない理由
- ランキングの表示形式や対象の投稿タイプを細かく制御したい。
- いくつかの人気のプラグインでは
postmeta
を使って閲覧数を記録し、ランキング機能を提供しているが、この方法では、1投稿あたり日数分のmeta_key
が蓄積されていくため、テーブルの肥大化によってクエリ速度が著しく低下する恐れがある。 データベース設計としてスマートではないし、拡張性や保守性の面でも大きなリスク となる。 - 表示速度やカスタマイズ性を重視し、SQLレベルでコントロールしたい。
実装方針
- 閲覧数は独自テーブルで管理(例:
wp_view_logs
) - 対象投稿タイプは
post
だけでなくカスタム投稿も含める - 集計はアクセスログベースで日付単位に記録
- 表示はテンプレート内で直接制御
- キャッシュ機能も付ける
閲覧数を記録する独自テーブルを作成
最初に、閲覧数を記録するための専用テーブル(view_logs
)を作成します。このテーブルを使って、投稿ID・投稿タイプ・閲覧日・閲覧数を効率的に保存できます。
// -------------------------------------
// 1. view_logs テーブルの作成(初回のみ)
// -------------------------------------
function create_view_log_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'view_logs';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
post_id BIGINT UNSIGNED NOT NULL,
post_type VARCHAR(50) NOT NULL,
viewed_at DATE NOT NULL,
count INT UNSIGNED NOT NULL DEFAULT 1,
KEY post_id (post_id),
KEY viewed_at (viewed_at)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
}
// 初回アクセス時にテーブル作成(実行済みならスキップ)
function check_and_create_post_views_table() {
if ( ! get_option('post_views_table_created') ) {
create_view_log_table();
update_option('post_views_table_created', 1);
}
}
add_action('init', 'check_and_create_post_views_table');
投稿が表示された際に閲覧数を記録する処理
投稿ページが表示されたときに、当日の閲覧数を独自テーブル(view_logs
)に記録します。同じ投稿が同日に複数回表示された場合はカウントを加算します。
// -------------------------------------
// 2. 投稿ページで閲覧数をカウント(日単位)
// -------------------------------------
function log_post_view() {
if (is_singular()) {
global $post, $wpdb;
$table = $wpdb->prefix . 'view_logs';
$date = current_time('Y-m-d');
$wpdb->query(
$wpdb->prepare(
"INSERT INTO $table (post_id, post_type, viewed_at, count)
VALUES (%d, %s, %s, 1)
ON DUPLICATE KEY UPDATE count = count + 1",
$post->ID, $post->post_type, $date
)
);
}
}
add_action('template_redirect', 'log_post_view');
ランキングの集計クエリ(日数で指定)
過去7日間や30日間など、指定した日数分の閲覧ログをもとに投稿を集計し、人気順に並べて取得する関数です。
// -------------------------------------
// 3. 閲覧数ランキングを取得する関数
// -------------------------------------
function get_popular_posts($days = 7, $post_type = 'post', $limit = 10) {
global $wpdb;
$table = $wpdb->prefix . 'view_logs';
$from_date = date('Y-m-d', strtotime("-{$days} days"));
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT post_id, SUM(count) AS total
FROM $table
WHERE post_type = %s AND viewed_at >= %s
GROUP BY post_id
ORDER BY total DESC
LIMIT %d",
$post_type, $from_date, $limit
)
);
return $results;
}
テンプレート内でランキングを表示する
取得したランキングデータをループしてHTMLで出力します。タイトルと閲覧数を表示し、リンクも自動で生成されます。
<ul class="popular-posts">
<?php foreach (get_popular_posts(7, 'custom_post_type') as $row): ?>
<?php $post = get_post($row->post_id); ?>
<li>
<a href="<?php echo get_permalink($post); ?>">
<?php echo esc_html(get_the_title($post)); ?>(<?php echo $row->total; ?> views)
</a>
</li>
<?php endforeach; ?>
</ul>
古い閲覧ログの削除(WP-Cron)
過去1年以上前の閲覧ログを自動で削除して、データベースの肥大化を防ぎます。WP-Cronを使って毎日深夜に実行されるように設定します(※削除する期間は必要に応じて変更してください)。
// -------------------------------------
// 4. 古い閲覧ログの削除
// -------------------------------------
function cleanup_old_view_logs() {
global $wpdb;
$table = $wpdb->prefix . 'view_logs';
$wpdb->query("DELETE FROM $table WHERE viewed_at < DATE_SUB(CURDATE(), INTERVAL 1 YEAR)");
}
add_action('wp_scheduled_cleanup_logs', 'cleanup_old_view_logs');
if (!wp_next_scheduled('wp_scheduled_cleanup_logs')) {
wp_schedule_event(time(), 'daily', 'wp_scheduled_cleanup_logs');
}
表示の高速化とキャッシュの活用
ランキング表示は頻繁に実行されるため、毎回クエリを実行するとサーバー負荷が高くなります。そのため、一定期間キャッシュを使ってデータを保持し、パフォーマンスを改善することが効果的です。
キャッシュの実装方法(transient APIの利用)
以下は、get_popular_posts()
の結果を1時間キャッシュする例です:
function get_cached_popular_posts($days = 7, $post_type = 'post', $limit = 10) {
$transient_key = "popular_posts_{$post_type}_{$days}_{$limit}";
$cached = get_transient($transient_key);
if ($cached !== false) {
return $cached; // キャッシュがある場合はそれを返す
}
// キャッシュがなければ集計処理を行う
global $wpdb;
$table = $wpdb->prefix . 'view_logs';
$from_date = date('Y-m-d', strtotime("-{$days} days"));
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT post_id, SUM(count) AS total
FROM $table
WHERE post_type = %s AND viewed_at >= %s
GROUP BY post_id
ORDER BY total DESC
LIMIT %d",
$post_type, $from_date, $limit
)
);
set_transient($transient_key, $results, HOUR_IN_SECONDS); // 1時間キャッシュ
return $results;
}
テンプレートでの利用例
<ul class="popular-posts">
<?php foreach (get_cached_popular_posts(7, 'custom_post_type') as $row): ?>
<?php $post = get_post($row->post_id); ?>
<li>
<a href="<?php echo get_permalink($post); ?>">
<?php echo esc_html(get_the_title($post)); ?>(<?php echo $row->total; ?> views)
</a>
</li>
<?php endforeach; ?>
</ul>
補足
- キャッシュ時間は
HOUR_IN_SECONDS
(1時間)を使っていますが、DAY_IN_SECONDS
(1日)などに調整可能です。 - 投稿の更新や新規投稿時に
delete_transient()
を使って手動でキャッシュをクリアする設計もできます。 - より高度なキャッシュには、オブジェクトキャッシュ(Redisなど)との連携も検討可能です。
まとめ:柔軟で高速なランキング表示を目指して
プラグインに頼らず独自テーブルを用いたランキング表示を自作することで、表示形式や集計対象の自由度を確保しつつ、パフォーマンスと保守性の両立が可能になります。サイト規模の拡大や将来的な分析活用も視野に入れた柔軟な設計としておすすめです。

Contact
ウェブサイトの制作や運用に関わる
お悩みやご相談
お気軽にお問い合わせ下さい
ウェブサイトと一口に言っても、企業サイトやECサイト、ブログ、SNSなど、その“カタチ”は目的に応じてさまざまであり、構築方法や使用する技術も大きく異なります。株式会社コナックスでは、お客様のご要望やブランドの個性を丁寧に汲み取り、最適なウェブサイトの“カタチ”をご提案いたします。
デザイン、ユーザビリティ、SEO対策はもちろん、コンテンツ制作やマーケティング戦略に至るまで、あらゆるフェーズでお客様のビジネスに寄り添い、成果につながるウェブサイトづくりをサポートいたします。私たちは、ウェブサイトの公開をゴールではなくスタートと捉え、お客様のビジネスの成功に向けて共に伴走してまいります。