WordPress:期間指定で人気記事を表示(プラグインなし)

WordPress:期間指定で人気記事を表示(プラグインなし)

現在は、WordPress Popular Posts を使って「よく読まれている記事」、いわゆる人気記事を表示しているのですが、「テーマをゼロから作ってみる」ということで、これもプラグインなしで出来ないかといろいろやってみようと思います。

で、「人気記事 プラグインなし」で検索しますとたくさんヒットするのですが、すべてカスタムフィールを使って表示回数を加算していくだけのもので、どれもコピペされた記事ばかりです。そうではなく、1日とか、1週間などと一定期間内の人気記事を表示するようにしてみようということです。

すでに現時点でも可能になっているのですが、プログラムとしてはちょっとみっともない状態のままですので完成版にはあと2回(2記事)くらい必要になりそうです(笑)。

01期間指定人気記事の構想

検索してヒットするカスタムフィールドを使うプログラムもそうですが、結局、人気記事とはその記事が何回表示されたかですので、記事ページが表示されたときに表示回数を加算していけばいいということになります。

ただ、これですと期間は全期間になってしまい、たとえば、現在から24時間前までの表示回数を取り出すことが出来ません。どういう場合にそうしたものが必要かは、別サイトの映画レビューサイト「そんなには褒めいないよ。映画評」がそうですが、いわゆるトレンド記事の意味合いで表示したい場合です。

ですので、まずは次の考え方で始めてみます。

02アクセス時間を独自テーブルに保存する

  • 記事 ID 、時間を保存できる独自テーブルを2つ作成する
  • (仮に)テーブル名を wp_imz_ppviewdata, wp_imz_ppdata とする
  • 投稿ページにアクセスがあれば、その2つのテーブルに記事 ID と現在時刻を保存する

保存するテーブルは、とりあえずは phpmyadmin を使って次のように作ります。 wp_imz_ppviewdata, wp_imz_ppdata まったく同じです。

sql ですと、

CREATE TABLE wp_imz_ppviewdata (
    id BIGINT AUTO_INCREMENT,
    postid BIGINT NOT NULL,
    view_datetime DATETIME NOT NULL,
    PRIMARY KEY (id)
);

これでテーブルが作成されます。

set_imz_popularposts

保存コードです。無茶苦茶シンプルです。

/* imz_popularposts 人気記事 */
function set_imz_popularposts($post_id) {
    //最終的にはログインユーザーはカウントしないように書き換え
    if(is_preview()) return;

    global $wpdb;
    $table_viewdata = 'wp_imz_ppviewdata';
    $table_ppdata = 'wp_imz_ppdata';
    
    //保存する配列
    $array = array(
        'postid' => $post_id,
        'view_datetime' => current_time('mysql')
    );

    // データを登録
    $wpdb->insert( $table_viewdata, $array, array('%d', '%s'));
    $wpdb->insert( $table_ppdata, $array, array('%d', '%s'));
}

このコードの呼び出しは、single.php の head タグの末尾辺りに次のコードを入れておきます。

<?php set_imz_popularposts(get_the_ID()); ?>

これで投稿ページにアクセスがある度にテーブルに1行ずつ保存されていきます。

03アクセス数を集計し出力する

同じ構造のテーブルを2つ作っている理由は、wp_imz_ppviewdata はすべてのアクセスの保存用、wp_imz_ppdata は指定期間のアクセス保存用です。これが必要か、また有効かはまだわかりませんが、wp_imz_ppdata は指定期間を越えるものは毎回アクセス時に削除してしまい、指定期間を変更する場合には wp_imz_ppviewdata からデータをコピーしようということです。最終的にどうなるかはまだわかりません。

  • 人気記事の表示ブロックから記事数を指定して get_imz_popularposts を呼び出す
  • get_imz_popularposts では wp_imz_ppdata の現在時刻から指定期間よりも以前のデータを削除する
  • 残った指定期間内のデータを postid で集計し、降順でソートし出力する
  • postid から各データを取得し配列に入れて呼び出し元に返す
  • 表示ブロックで表示する

get_imz_popularposts

function get_imz_popularposts($limit) {
    global $wpdb;
    $table_viewdata = 'wp_imz_ppviewdata';
    $table_ppdata = 'wp_imz_ppdata';
    $now = current_time('mysql');
    $interval = '1 DAY';

    $query = "DELETE FROM " . $table_ppdata . " WHERE view_datetime < DATE_SUB(%s, INTERVAL " . $interval . ")";
    $wpdb->query( $wpdb->prepare( $query, $now ));

    $query = "SELECT postid, count(postid) FROM " . $table_ppdata . " GROUP BY postid order by count(postid) desc;";
    $results = $wpdb->get_results( $wpdb->prepare( $query ) );

    $ppdata = array();
    $counter = 0;
    foreach ($results as $row){
        $id = (int)$row->postid;
        if (has_post_thumbnail($id)){
            $featured_image = get_the_post_thumbnail( $id, 'full' );
        }else{
            $featured_image = '<img src="' . esc_url(get_stylesheet_directory_uri() . '/images/dummy.jpg') . '" />';
        }
        $content = mb_substr( strip_tags( get_post($id)->post_content ), 0, 60, 'UTF-8' ) . '...';
        $ppdata[] = ['title' => get_the_title($id), 
            'permalink' => get_permalink($id), 
            'image' => $featured_image, 
            'date_iso' => get_the_date('c', $id), 
            'date_default' => get_the_date('', $id),
            'category' => get_the_category_list( ',', 'multiple', $id ), 
            'content' => $content];
        $counter++;
        if($counter >= $limit) break;
    }
    return $ppdata;
}

戻り値は、このコードのように各データを配列にして返すか、postid だけにして表示コードで各データを呼び出すかはまだどうなるかはわかりません。

現時点では次の表示コードでホームページ(front_page.php)に出力しています。

<section id="popularpost-articles" class="front-page-articles">
    <h2 class="section-title">よく読まれている記事</h2>
    <?php $ppdata = get_imz_popularposts(5); foreach ($ppdata as $data): ?>
    <article class="entry">
        <div class="thumbnail">
            <a href="<?php echo esc_url($data['permalink']); ?>"><?php echo $data['image']; ?></a>
        </div>
        <header class="entry-header">
            <h2 class="entry-title"><a href="<?php echo esc_url($data['permalink']); ?>"><?php echo esc_html($data['title']); ?></a></h2>
            <p class="date"><?php echo esc_attr($data['date_default']); ?></p>
            <p class="category"><?php echo $data['category']; ?></p>
        </header>
        <div class="entry-body"><?php echo esc_html($data['content']); ?></div>
    </article>
    <?php endforeach; ?>
</section>

ということで、次のようにホームページに表示されています。

これでなんとかいけそうです。

あとはバグ取りとプログラムの整理とセキュリティチェックですかね。それにクラス化したほうがいいかも知れません。

プラグイン版と functions.php に置いておくクラス版が完成しました。