WordPressで人気記事ランキングを表示する際、プラグインに頼らずに実装したい場面があります。この記事では、ページキャッシュ環境でも正確にカウントできる方法で、アクセス数を記録し人気記事ランキングを表示する方法を解説します。
この記事でわかること
- wp_headでの直接カウントがなぜキャッシュ環境で機能しないか
- Ajaxを使った、キャッシュされたページでも正確なカウント方法
- Cookieによる重複カウントの防止
なぜwp_headで直接カウントしてはいけないか
ページキャッシュがあるとカウントが止まる
add_action('wp_head', 'track_post_views') のように、ページ読み込み時にPHPで直接カウントする実装がよく紹介されますが、WP Super Cache・WP Rocket・Cocoonの高速化設定などでページ全体がキャッシュされて配信される場合、そのリクエストではPHPが実行されずカウント処理も一切走りません。その結果、キャッシュが効いている間はビュー数が増えず、実際のアクセス数と大きく乖離した「人気記事ランキング」になってしまいます。ページ自体はキャッシュしたまま、カウントだけを確実に行うにはAjax(admin-ajax.php)を使うのが確実です。admin-ajax.php は通常キャッシュの対象外のため、ページがキャッシュ配信されてもJavaScriptがブラウザ側で実行されカウントを送信できます。Ajaxでビューカウントを送信する仕組みを作る
まず、個別投稿ページでスクリプトを読み込み、Ajax送信に必要な情報(URL・投稿ID・nonce)を渡します。
functions.php:スクリプトの読み込みとAjax情報の受け渡し
function enqueue_post_views_script() {
if ( ! is_single() ) {
return;
}
wp_enqueue_script( 'post-views-counter', get_theme_file_uri( '/js/post-views.js' ), array(), '1.0', true );
wp_localize_script( 'post-views-counter', 'postViewsData', array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'post_id' => get_the_ID(),
'nonce' => wp_create_nonce( 'post_views_nonce' ),
) );
}
add_action( 'wp_enqueue_scripts', 'enqueue_post_views_script' );
次に、js/post-views.js でCookieを確認し、未カウントであればAjaxを送信します。
js/post-views.js:Cookieで重複防止しつつAjax送信
(function () {
if ( typeof postViewsData === 'undefined' ) {
return;
}
var cookieName = 'post_viewed_' + postViewsData.post_id;
// 既にこの投稿をカウント済みなら何もしない
if ( document.cookie.indexOf( cookieName + '=' ) !== -1 ) {
return;
}
fetch( postViewsData.ajax_url, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'action=increment_post_views&post_id=' + encodeURIComponent( postViewsData.post_id )
+ '&nonce=' + encodeURIComponent( postViewsData.nonce ),
} ).then( function () {
document.cookie = cookieName + '=1; path=/; max-age=' + ( 60 * 60 * 24 ); // 1日は再カウントしない
} );
} )();
最後に、Ajaxリクエストを受け取ってカウントする処理を追加します。get→インクリメント→updateという手順は同時アクセス時に更新が取りこぼされる可能性があるため、データベースを直接アトミックに更新します。
functions.php:Ajaxリクエストを受けてアトミックにカウント
function handle_increment_post_views() {
check_ajax_referer( 'post_views_nonce', 'nonce' );
$post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
if ( ! $post_id || 'publish' !== get_post_status( $post_id ) ) {
wp_send_json_error();
}
global $wpdb;
$count_key = '_post_views_count';
$updated = $wpdb->query( $wpdb->prepare(
"UPDATE {$wpdb->postmeta} SET meta_value = meta_value + 1
WHERE post_id = %d AND meta_key = %s",
$post_id,
$count_key
) );
if ( ! $updated ) {
add_post_meta( $post_id, $count_key, 1, true );
}
wp_send_json_success();
}
add_action( 'wp_ajax_increment_post_views', 'handle_increment_post_views' );
add_action( 'wp_ajax_nopriv_increment_post_views', 'handle_increment_post_views' );
Ajax方式は単純なボットも自然に除外できる
多くの単純なクローラーボットはJavaScriptを実行しないため、Ajaxでカウントする方式にするだけでそれらのアクセスがカウントに混ざりにくくなります(副次的な効果であり、高度なボット対策そのものではありません)。
多くの単純なクローラーボットはJavaScriptを実行しないため、Ajaxでカウントする方式にするだけでそれらのアクセスがカウントに混ざりにくくなります(副次的な効果であり、高度なボット対策そのものではありません)。
人気記事を取得する関数を作成
アクセス数の多い順に投稿を取得するための関数を定義します。
functions.php:アクセス数上位の投稿を取得
function get_popular_posts( $limit = 5 ) {
$args = array(
'post_type' => 'post',
'posts_per_page' => $limit,
'meta_key' => '_post_views_count',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'ignore_sticky_posts' => true,
);
return new WP_Query( $args );
}
人気記事をテンプレートに表示
表示したいテンプレートファイル(例:sidebar.php)に以下のコードを記述します。
sidebar.php:人気記事ランキングを表示
<?php
$popular_posts = get_popular_posts( 5 );
if ( $popular_posts->have_posts() ) {
echo '<ul class="popular-posts">';
while ( $popular_posts->have_posts() ) {
$popular_posts->the_post();
$views = get_post_meta( get_the_ID(), '_post_views_count', true );
echo '<li><a href="' . esc_url( get_permalink() ) . '">'
. esc_html( get_the_title() ) . '</a>(' . intval( $views ) . ' views)</li>';
}
echo '</ul>';
wp_reset_postdata();
} else {
echo '<p>人気記事が見つかりませんでした。</p>';
}
?>
よくある質問(FAQ)
Qプラグインなしで記事のPV数を記録・表示するには?
AAjax経由でカスタムフィールドをインクリメントする方法が、ページキャッシュ環境でも確実にカウントできます。WP_Queryのmeta_keyとorderby=meta_value_numで上位を取得して表示します。
QPVカウントの際にボットや繰り返しアクセスを除外するには?
ACookieでリピートアクセス(同一訪問者の再カウント)を判定するのが基本です。Ajax方式にすることで、JavaScriptを実行しない単純なボットのアクセスも自然に除外されやすくなります。完全なボット判定が必要な場合はUser-Agent判定等と組み合わせますが、偽装される可能性もあるため過信は禁物です。
Q人気記事をサイドバーにウィジェットとして表示するには?
AWP_Widgetクラスを継承したカスタムウィジェットを作成し、register_widget()で登録します。サイドバーのウィジェットエリアで
dynamic_sidebar()が呼ばれていることを確認してください。まとめ
- 避ける:wp_headでの直接カウント(ページキャッシュ環境で機能しない)
- 推奨:Ajax(admin-ajax.php)経由でのカウント
- あわせて:Cookieで重複カウントを防止し、DBは直接UPDATEでアトミックに更新する
プラグインを使わずにアクセス数を記録し人気記事ランキングを表示できますが、キャッシュ環境を前提にした設計にしておくことが重要です。
