WordPressで前日のPVを自前でカウントするぞ

シェアする:

この記事をおすすめしたい人

  • WordPressで前日PVを集計したい人
  • アナリティクスに頼らず自分でカウントしたい人
  • つまりオレ

WordPressで前日のPVを集計する方法を考えていきます。 「人気の投稿」とかをソートするときにリアルタイム性が出やすくするための数値ですね。

以前同じことをアナリティクスのAPIを使ってやりました。

ところがこの後アナリティクスがメジャーバージョンアップしてしまって使えなくなって、サイトまるごと停止してしまってたので、今回は外部に頼らずにfunctions.phpに書いていこうと思います。

ChatGPTがな!

処理の大まかな流れ

処理の流れはこう↓決めました。

  1. 閲覧ごとに当日のPVをカウント
  2. 24時になったら当日PVを前日PVに移して当日PVを初期化

PVのカウントはWP-PostViewsってのを使ってて、ソース見ればやり方わかるでしょう。

毎日24時で処理するのはCRONだっけ、なんかあったと思います。 全然わかんないのでChatGPTくんに聞こう。

あとは、投稿一覧の画面で表示したいな。 できればソート機能もつけたいな。

みたいな感じでやってこうと思います。

先に必要な定数を定義

こんな↓感じで処理に必要な定数を決めました。


//当日のPVを記録するカスタムフィールドの名前
define('TODAY_PV_KEY', 'today_pv_count');
//前日のPVを記録するカスタムフィールドの名前
define('YESTERDAY_PV_KEY', 'yesterday_pv_count');

//PVリセットのスケジュール用フックの名前
define('PV_RESET_HOOK', 'reset_pv_count');

//PVリセットを実行する時間
define('PV_RESET_TIME', 'today 24:00');

//投稿一覧に表示する「当日PV」列の名前
define('TODAY_PV_COLUMN', 'today_pv');
//投稿一覧に表示する「前日PV」列の名前
define('YESTERDAY_PV_COLUMN', 'yesterday_pv');

ハードコードを避けるためなので好きな名前でおkです。

PV_RESET_TIMEは集計を終える時間で、上だと深夜0時に実行されます。 これだと夜更かししてる人を考慮できないので僕は「tomorrow 5:00 am」で明け方5時にしています。

まずはPVをカウントする

WP-PostViewsのソースを見たらwp_headでフックしてたので真似してこう↓なりました。


add_action('wp_head', function () {
  if ( is_user_logged_in() || !is_singular() ) return;
  
  global $post;
  
  $id = $post ? $post->ID : null;
  
  if ( empty($id) ) return;
  
  $pv = get_post_meta($id, TODAY_PV_KEY, true);
  
  if ( empty($pv) ) {
    add_post_meta($id, TODAY_PV_KEY, '0');
    add_post_meta($id, YESTERDAY_PV_KEY, '0');
  } else {
    update_post_meta($id, TODAY_PV_KEY, $pv+1);
  }
});

「is_user_logged_in」でログインユーザをカウントしないようにしています。

「is_singular」というのは投稿ページ、固定ページ、カスタム投稿タイプで真が返る関数らしくてChatGPTくんに教えてもらいました。 詳細は調べてません。

次に現在の投稿を取得してIDが取得できれば処理続行。 取得できなければ終了。

現在の投稿から当日PVのカスタムフィールドを取得。 取得できればカウントを+1して保存。 取得できなければ初アクセスということで、当日PVと前日PVのカスタムフィールドを追加。

指定した時間でPVをリセット

まず実行される処理部分↓を作っておきます。


add_action(PV_RESET_HOOK, function () {
  $posts = get_posts(array(
    'post_type'      => 'any',
    'posts_per_page' => -1,
    'fields'         => 'ids',
  ));
  
  foreach ( $posts as $id ) {
    $pv = get_post_meta($id, TODAY_PV_KEY, true);
    
    update_post_meta($id, YESTERDAY_PV_KEY, $pv);
    update_post_meta($id, TODAY_PV_KEY, '0');
  }
});

全投稿を取得してループを回し、当日PVを取得。 それを前日PVにセット、当日PVを初期化です。

これだと投稿数1000とかいくと重くならない?って聞くとやっぱりなるらしくて、その場合はSQLで直接いじればいいよって教えてもらったんですがSQL苦手なので今回は断念。

次にこの処理をスケジュールにセット↓します。


if ( !wp_next_scheduled(PV_RESET_HOOK) ) {
  wp_schedule_event(strtotime(PV_RESET_TIME), 'daily', PV_RESET_HOOK);
}

wp_next_scheduledでスケジュールに登録済みかをチェックし、されてなければwp_schedule_eventで登録してます。 「daily」の部分は周期を表すそうで、週1とか月1とかもできるらしいです。

投稿一覧で表示

この辺ほとんどやったことないので全部ChatGPTくん任せです。 簡単な動作確認はしています。

まず投稿一覧に表示列を追加↓します。


add_filter('manage_posts_columns', function($columns) {
  $columns[TODAY_PV_COLUMN] = '当日PV';
  $columns[YESTERDAY_PV_COLUMN] = '前日PV';
  
  return $columns;
});

今考えるとここも定数定義しとくべきでしたね。

次に個々の投稿で追加した列に表示する部分↓を作ります。


add_action('manage_posts_custom_column', function($name, $id) {
  if ( $name == TODAY_PV_COLUMN ) {
    $pv = get_post_meta($id, TODAY_PV_KEY, true);
    
    echo $pv ? $pv : '0';
  }
  if ( $name == YESTERDAY_PV_COLUMN ) {
    $pv = get_post_meta($id, YESTERDAY_PV_KEY, true);
    
    echo $pv ? $pv : '0';
  }
}, 10, 2);

次に登録した列をソート可能な列として登録します。


add_filter('manage_edit-post_sortable_columns', function($columns) {
  $columns[TODAY_PV_COLUMN] = TODAY_PV_COLUMN;
  $columns[YESTERDAY_PV_COLUMN] = YESTERDAY_PV_COLUMN;
  
  return $columns;
});

「manage_edit-post_sortable_columns」は「投稿ページ」専用なので、固定ページの場合は「manage_edit-page_sortable_columns」に変更するか、それ用を追加しないといけません。

最後にソートの処理を追加↓して終わり。


add_action('pre_get_posts', function($query) {
  if ( !is_admin() ) return;
  
  $orderby = $query->get('orderby');
  
  if ( $orderby == TODAY_PV_COLUMN ) {
    $query->set('meta_key', TODAY_PV_KEY);
    $query->set('orderby', 'meta_value_num');
  } elseif ( $orderby == YESTERDAY_PV_COLUMN ) {
    $query->set('meta_key', YESTERDAY_PV_KEY);
    $query->set('orderby', 'meta_value_num');
  }
});

まとめ

ここまでのコードをまとめるとこう↓なります。


define('TODAY_PV_KEY', 'today_pv_count');
define('YESTERDAY_PV_KEY', 'yesterday_pv_count');
define('PV_RESET_HOOK', 'reset_pv_count');
define('PV_RESET_TIME', 'today 24:00');
define('TODAY_PV_COLUMN', 'today_pv');
define('YESTERDAY_PV_COLUMN', 'yesterday_pv');

add_action('wp_head', function () {
  if ( is_user_logged_in() || !is_singular() ) return;
  
  global $post;
  
  $id = $post ? $post->ID : null;
  
  if ( empty($id) ) return;
  
  $pv = get_post_meta($id, TODAY_PV_KEY, true);
  
  if ( empty($pv) ) {
    add_post_meta($id, TODAY_PV_KEY, '0');
    add_post_meta($id, YESTERDAY_PV_KEY, '0');
  } else {
    update_post_meta($id, TODAY_PV_KEY, $pv+1);
  }
});

add_action(PV_RESET_HOOK, function () {
  $posts = get_posts(array(
    'post_type'      => 'any',
    'posts_per_page' => -1,
    'fields'         => 'ids',
  ));
  
  foreach ( $posts as $id ) {
    $pv = get_post_meta($id, TODAY_PV_KEY, true);
    
    update_post_meta($id, YESTERDAY_PV_KEY, $pv);
    update_post_meta($id, TODAY_PV_KEY, '0');
  }
});

if ( !wp_next_scheduled(PV_RESET_HOOK) ) {
  wp_schedule_event(strtotime(PV_RESET_TIME), 'daily', PV_RESET_HOOK);
}

add_filter('manage_posts_columns', function($columns) {
  $columns[TODAY_PV_COLUMN] = '当日PV';
  $columns[YESTERDAY_PV_COLUMN] = '前日PV';
  
  return $columns;
});

add_action('manage_posts_custom_column', function($name, $id) {
  if ( $name == TODAY_PV_COLUMN ) {
    $pv = get_post_meta($id, TODAY_PV_KEY, true);
    
    echo $pv ? $pv : '0';
  }
  if ( $name == YESTERDAY_PV_COLUMN ) {
    $pv = get_post_meta($id, YESTERDAY_PV_KEY, true);
    
    echo $pv ? $pv : '0';
  }
}, 10, 2);

add_filter('manage_edit-post_sortable_columns', function($columns) {
  $columns[TODAY_PV_COLUMN] = TODAY_PV_COLUMN;
  $columns[YESTERDAY_PV_COLUMN] = YESTERDAY_PV_COLUMN;
  
  return $columns;
});

add_action('pre_get_posts', function($query) {
  if ( !is_admin() ) return;
  
  $orderby = $query->get('orderby');
  
  if ( $orderby == TODAY_PV_COLUMN ) {
    $query->set('meta_key', TODAY_PV_KEY);
    $query->set('orderby', 'meta_value_num');
  } elseif ( $orderby == YESTERDAY_PV_COLUMN ) {
    $query->set('meta_key', YESTERDAY_PV_KEY);
    $query->set('orderby', 'meta_value_num');
  }
});

PV_RESET_TIMEで更新時間だけ設定したらこのままコピペで使えると思います。 24時でよければこのままでおk。

シェアする: