WordPress:人気記事を直近の期間指定で表示する

WordPress:人気記事を直近の期間指定で表示する

前記事「WordPress:期間指定で人気記事を表示(プラグインなし)」でざっと作ったプログラムをもうちょっとマシなコードに書き換え、プラグイン版とクラス版を作りました。「テーマをゼロから作ってみる」プロジェクトの一環でもあります。

01プログラムの概要

WordPress で人気記事(アクセス数の多い記事)を表示する場合、プラグインの WordPress Popular Posts を使う場合が多いかと思います。また、ただ単に全期間の人気記事を表示するのであれば、ネット上にカスタムフィールドを使った簡易なプログラムがたくさんあります。みな同じプログラムですのでコピペだと思います。

ただ、これですと全期間のアクセス数でしかカウントできませんので、ある一定期間、例えば過去1日のアクセスランキングなどのトレンド的な人気記事は取り出せません。

そこで、期間指定できる人気記事プログラムを作ってみました。その構想版が前記事であり、今回それをプラグイン化したものとクラスにまとめて functions.php に置いておける版にまとめました。

02期間指定人気記事プラグイン

いろいろやってみた結果、最終的にはこのプラグインを使わずに functions.php にクラスを置くことにしましたので、次の記事にあるデータベース構造のバージョン管理は省略しています。

Plugin Name: imz Popular Posts

wp-content\plugins フォルダに適当なフォルダを作ってその中に次のコードを入れて、プラグインを有効にすれば使えます。

<?php
/*
Plugin Name: imz Popular Posts
Description: 期間を指定してアクセス数の多い記事を表示するプラグイン
Author: imuza.com
Version: 1.0.0
Author URI: https://imuza.com
*/

function create_custom_table() {
    global $wpdb;
    $charset_collate = $wpdb->get_charset_collate();
    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
	
    $table_name_ppviewdata = $wpdb->prefix . 'imz_ppviewdata';
    $table_name_ppdata = $wpdb->prefix . 'imz_ppdata';

    $sql = "CREATE TABLE " . $table_name_ppviewdata . " (
    	id bigint(20) NOT NULL AUTO_INCREMENT,
	postid bigint(20) NOT NULL,
	view_datetime datetime NOT NULL,
	PRIMARY KEY  (id),
	KEY view_datetime (view_datetime)
    ) $charset_collate;";
    dbDelta($sql);

    $sql = "CREATE TABLE " . $table_name_ppdata . " (
    	id bigint(20) NOT NULL AUTO_INCREMENT,
    	postid bigint(20) NOT NULL,
    	view_datetime datetime NOT NULL,
    	PRIMARY KEY  (id),
    	KEY view_datetime (view_datetime)
    ) $charset_collate;";
    dbDelta($sql);
}
register_activation_hook(__FILE__, 'create_custom_table');

function uninstall_imz_popular_posts_plugin(){
    global $wpdb;
    $table_name = $wpdb->prefix . 'imz_ppviewdata';
    $wpdb->query("DROP TABLE IF EXISTS $table_name"); 
    $table_name = $wpdb->prefix . 'imz_ppdata';
    $wpdb->query("DROP TABLE IF EXISTS $table_name"); 
}
register_uninstall_hook(__FILE__,'uninstall_imz_popular_posts_plugin');

function set_imz_popularposts() {
    if(is_user_logged_in() || !is_single()) return;

    global $wpdb;
    $table_viewdata = $wpdb->prefix . 'imz_ppviewdata';
    $table_ppdata = $wpdb->prefix . 'imz_ppdata';
    
    //保存する配列
    $array = array(
        'postid' => get_the_ID(),
        'view_datetime' => current_time('mysql')
    );

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

function get_imz_popularposts($limit = 5) {
    global $wpdb;
    $table_viewdata = $wpdb->prefix . 'imz_ppviewdata';
    $table_ppdata = $wpdb->prefix . 'imz_ppdata';
    $now = current_time('mysql');

    // 表示する期間
    // https://dev.mysql.com/doc/refman/5.6/ja/date-and-time-functions.html#function_date-add 参照
    $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 ) );

    $pp_id = array();
    $counter = 0;
    foreach ($results as $row){
        $id = (int)$row->postid;
        $pp_id[] = $id;
        $counter++;
        if($counter >= $limit) break;
    }
    return $pp_id;
}

表示する期間の指定は、get_imz_popularposts() 内の $interval で指定します。プラグインですので本当は管理画面を作り期間を設定できるようにすべきなんですが、私の使用目的では一度設定すれば変更の可能性はほとんどありませんので面倒だなあとここまでになっています(笑)。

ということもあり、最終的に functions.php 内で管理できるクラス版にしたということです。

プラグインの使い方

single.php や front-page.php などで次のように呼び出せます。

$ids = get_imz_popularposts(5);

戻り値は引数で指定した数の降順投稿IDです。初期値は 5 です。ですので次のように WP_Query のインスタンスにパラメータを渡せば通常のループと同じように扱えます。

<?php
$ids = get_imz_popularposts(5);
if(!empty($ids)) :
	$query = new WP_Query(
		array(
			'post__in' => $ids,
			'orderby' => 'post__in'
        )
    );
	if ( $query->have_posts() ) :
		while ( $query->have_posts() ) : $query->the_post();
?>
<article class="entry">
	<div class="thumbnail">
		<a href="<?php the_permalink(); ?>"><?php has_post_thumbnail() ? the_post_thumbnail() : get_dummy_img(''); ?></a>
	</div>
	<header class="entry-header">
		<h2 class="entry-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
		<p class="date"><?php the_date(); ?></p>
		<p class="category"><?php the_category( ', ', 'multiple'); ?></p>
	</header>
    <div class="entry-body"><?php echo mb_substr( strip_tags( get_the_content() ), 0, 60, 'UTF-8' ) . '...'; ?></div>
</article>
<?php 
		endwhile;
	 endif;
?>
<?php wp_reset_postdata(); ?>
<?php endif; ?>

post__in パラメータは配列が空ですと true を返し、すべての記事から表示してしまいますので !empty($ids) で空かどうかを判定しています。

03期間指定できる人気記事表示クラス

考え方はプラグイン版とまったく同じです。

次のコードを functions.php に入れます。

/**
 * imz Popular Posts 期間を指定してアクセス数の多い記事を表示するクラス
 * @param string $interval 期間 MySQL日付および時間関数による
 * @return array|int 記事IDの配列 アクセス数降順
 */
class imzPopularPosts {

    public $interval;
    private $ppviewdata;
    private $ppdata;

    public function __construct( $arg = '1 DAY' ){
      global $wpdb;
      // テーブル名
      $this->ppviewdata = $wpdb->prefix . 'imz_ppviewdata';
      $this->ppdata = $wpdb->prefix . 'imz_ppdata';
      // 期間
      $this->interval = $arg;
      // テーブル作成
      $this->create_custom_table();
      // 投稿ページへのアクセスをデータベースに保存するフック
      add_action('wp_head', array($this, 'set_imz_popularposts'));
    }

    function create_custom_table() {
        global $wpdb;
        if($wpdb->get_var("SHOW TABLES LIKE '$this->ppviewdata'") == $this->ppviewdata) return;
        $charset_collate = $wpdb->get_charset_collate();
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        
        $sql = "CREATE TABLE " . $this->ppviewdata . " (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            postid bigint(20) NOT NULL,
            view_datetime datetime NOT NULL,
            PRIMARY KEY  (id),
            KEY view_datetime (view_datetime)
        ) $charset_collate;";
        dbDelta($sql);
    
        $sql = "CREATE TABLE " . $this->ppdata . " (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            postid bigint(20) NOT NULL,
            view_datetime datetime NOT NULL,
            PRIMARY KEY  (id),
            KEY view_datetime (view_datetime)
        ) $charset_collate;";
        dbDelta($sql);
    }

    function set_imz_popularposts() {
        if(is_user_logged_in() || !is_single()) return;
            
        global $wpdb;
        
        //保存する配列
        $array = array(
            'postid' => get_the_ID(),
            'view_datetime' => current_time('mysql')
        );
    
        // データを登録
        $wpdb->insert( $this->ppviewdata, $array, array('%d', '%s'));
        $wpdb->insert( $this->ppdata, $array, array('%d', '%s'));
    }

    /**
     * @param int $limit 表示記事数
     */
    function get_imz_popularposts( $limit = 5 ) {
        global $wpdb;
        $now = current_time('mysql');
    
        // 指定期間以前のデータを削除
        $query = "DELETE FROM " . $this->ppdata . " WHERE view_datetime < DATE_SUB(%s, INTERVAL " . $this->interval . ")";
        $wpdb->query( $wpdb->prepare( $query, $now ));
    
        // 投稿IDごとに降順で集計
        $query = "SELECT postid, count(postid) FROM " . $this->ppdata . " GROUP BY postid order by count(postid) desc;";
        $results = $wpdb->get_results( $wpdb->prepare( $query ) );

        // 投稿IDを整数に変換し返す
        $ppid = array();
        $counter = 0;
        foreach ($results as $row){
            $ppid[] = (int)$row->postid;
            $counter++;
            if($counter >= $limit) break;
        }
        return $ppid;
    }

}
$imz_ppid = new imzPopularPosts();

人気記事クラスの使い方

functions.php と同レベルのテンプレートファイルであれば次のように使います。

<?php
$ids = $imz_ppid->get_imz_popularposts(5);
if(!empty($ids)) :
    $query = new WP_Query(
        array(
         'post__in' => $ids,
         'orderby' => 'post__in'
     )
    );
    if ( $query->have_posts() ) :
        while ( $query->have_posts() ) : $query->the_post();
    ?>
<article class="entry">
    <div class="thumbnail">
        <a href="<?php the_permalink(); ?>"><?php has_post_thumbnail() ? the_post_thumbnail() : get_dummy_img(''); ?></a>
    </div>
    (略)
</article>
<?php
        endwhile;
    endif; ?>
<?php wp_reset_postdata(); ?>
<?php endif; ?>

ただし、functions.php と同レベルのテンプレートファイルから呼び出される、たとえば get_sidebar() などのテンプレートファイルでは functions.php のインスタンス(上の例では $imz_ppid )が渡りません。

その場合は、次のように引数で渡すか、新たにインスタンスを生成する必要があります。

<?php // たとえば、single.php で ?>
<?php $args = ['pp' => $imz_ppid]; get_sidebar(null, $args); ?>
<?php // と変数を渡し sidebar.php で次のように受け取る ?>
<?php
    $ids = $args['pp']->get_imz_popularposts(5);
    //(略)
?>
<?php // または新たにインスタンスを生成する ?>
<?php
    $imz_ppid = new imzPopularPosts();
    $ids = $imz_ppid->get_imz_popularposts(5);
    //(略)
?>

これで超シンプルに表示期間を指定できる人気記事プログラムが完成です。

テーマをゼロから作ってみる」プロジェクトも終了間近でしょうか…(笑)。