コフス技術ブログ

WordPress Popular PostsにてWPP_Queryクラスを用いて人気記事を出力するあれこれ

WordPressにて人気記事の出力が可能なプラグインと言えばWordPress Popular Posts(※ 以下WPP)が有名だと思います。古くからあるプラグインで、今なおメンテナンスも継続されている事から多くのサイトで使われています。

そんなWPP、バージョン4.0からWPP_Queryクラスが追加されており、このWPP_Queryクラスを用いることでWordPressの投稿requestを取得する標準クラスのwp_queryライクな使い方が可能になりました。

これまでだとショートコード等を駆使して無理やりwp_queryライクに使えるよう関数を作ったりするのが定番でしたが、今やWPP_Queryのみで簡単に人気記事の投稿を取得出来てしまいます。

というわけで今回はその例のご紹介です。


そもそもWPP_Queryクラスだけで投稿まで取得出来ますが、まずはあくまでもwp_queryライクに使える様投稿の取得はWordPress標準クラスで取得し、WPP_Queryではwp_queryに渡す引数を取得する例で実装してみます。

/**
 * Wppからwp_query用args取得.
 * wppでもwp_queryと同じ感覚で出力できるようidを取得し、wp_queryで用いる形でargsを出力します.
 *
 * @param string $post_type カスタム投稿を指定。デフォルトnull.
 * @param string $range |day|weekly|month|から集計期間を選択.
 * @param int    $limit 取得する件数のリミットを指定.
 * @return $wpp_id idを返す.
 */
function c_get_wpp_args( $post_type = '', $range = 'weekly', $limit = 5 ) {

  $wpp_args = array(
    'range'     => $range,
    'order_by'  => 'views',
    'post_type' => $post_type,
    'limit'     => $limit,
  );

  $wpp_query = new WPP_Query( $wpp_args );
  $wpp_posts = $wpp_query->get_posts();

  $wpp_id = array();
  if ( $wpp_posts ) {
    foreach ( $wpp_posts as $value ) {
      array_push( $wpp_id, intval( $value->id ) );
    }
  }

  $args = array(
    'post_type'      => $post_type,
    'posts_per_page' => $limit,
    'orderby'        => 'post__in',
    'order'          => 'DESC',
    'post_status'    => 'publish',
    'post__in'       => $wpp_id,
    'post__not_in'   => get_option( 'sticky_posts' ),
  );

  return $args;
}

上記の例のc_get_wpp_argsは以下の引数を持ちます。

  • $post_type:投稿タイプ
  • $range:期間(day / weekly / month)
  • $limit:取得数

$rangeや$limitはWPP_Query特有の引数ですね。1日毎のランキングを取得したい場合はday、一週間単位ならweeklyといった感じで指定していきます。

肝になるのは以下の部分です。一旦WPP_Queryクラスで投稿を取得し、foreachでループを回していますがその際に投稿idを配列に入れていきます。

$wpp_query = new WPP_Query( $wpp_args );
$wpp_posts = $wpp_query->get_posts();

$wpp_id = array();
if ( $wpp_posts ) {
    foreach ( $wpp_posts as $value ) {
        array_push( $wpp_id, intval( $value->id ) );
    }
}

// array_mapを使うならこんな感じもあり.
$wpp_id = array_map(
  function( $wppost ) {
    return (int)$wppost->id;
  }, $wpp_query->get_posts()
);

投稿idの配列はその下の$argsのpost__inで使用しています。こうすることでWPP_Queryにて取得した投稿と同じ投稿を取得出来るwp_query用の引数が出来上がります。

あとはこれを通常通りループを回してしまえばwp_queryライクな使い方が出来るというわけです。

<?php
$query = new WP_Query( c_get_wpp_args( 'blog', 'weekly', 3 ) );

if ( $query->have_posts() ) {
    while ( $query->have_posts() ) {
        $query->the_post();
        get_template_part( 'template/item-blog' );
    }
} else {
    get_template_part( 'inc/parts-nopost' );
}
wp_reset_postdata();
?>

上記方法のメリットはget_template_partで読み込むテンプレートの中身を通常ループと同じ内容で利用できるという点です。
通常のループでも、WPPの投稿を取得するループでも全く同じテンプレートを読み込むことが出来るのが1番のメリットと言っていいでしょう。

一方デメリットとしてはWPP_Querywp_queryの2回クエリを発行している点です。無駄と言えば完全に無駄です。
というのも前述した通りWPP_Queryだけでも投稿の取得は勿論可能で、以下の様にすることでループを回すことが出来ます。

$wpp_args = array(
'range'     => $range,
'order_by'  => 'views',
'post_type' => $post_type,
'limit'     => $limit,
);

$wpp_query = new WPP_Query( $wpp_args );
$wpp_posts = $wpp_query->get_posts();

if ( $wpp_posts ) {
    foreach ( $wpp_posts as $wpp_post ) {

        $post_id = $wpp_post -> id; // 投稿ID
        get_template_part( 'template/item-blog' );

    }
}

ただ$wpp_post -> idといった様に通常のループwp_queryで取得できる形と異なってしまう為、読み込むテンプレート内でのidの取得等は$post->IDではなく、別途変数を用意するなりしてWPPの時でもidが取得できる様に工夫する必要がありそうです。


一旦idだけ取得してその後wp_queryで取得するのか、それともWPP_Queryだけでスマートに投稿を取得するのか、用途に合わせた実装を選んでみて下さい。