Nuxt.jsで人気記事一覧を作る2つの方法(WordPress REST API)

シェアする:

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

  • WordPressからNuxt.jsへ移行を考えている人
  • Nuxt.jsで人気記事一覧を作りたい人
  • つまりオレ

WordPress Nuxt化の一環です。

今回はWordPressのREST APIから取得した記事一覧をPV順に表示する、いわゆる「人気記事」の作り方を考えたいと思います。

WordPressの上でな!

※ このブログはまだWordPress製です

PVのカウントはどうするのか

WordPress側のプラグインに任せます。

僕が知ってるのだと「WP-PostViews」。

僕はなんでも自分でやろうとすることが多いので一般的な人気プラグインには疎いですが、他にもいろいろありそう。

PV順でソートの意味するところ

これはつまり、WordPressのカスタムフィールドでソートすることを意味します。

WP-PostViewsの場合だと「views」というカスタムフィールドにPVが記憶されます。

他のPV系プラグインもおそらくカスタムフィールドを使っているんでしょう。

WordPressのREST APIから、カスタムフィールドでソートした記事一覧を取得できれば、Nuxt.jsでも人気記事一覧が作れます。

参考に、WordPress上だとこんな↓感じでPV順のソートができます(WP-PostViewsの場合)。


$query = array(
  'orderby' => 'meta_value_num',
  'meta_key' => 'views',
  'order' => 'DESC',
);

$posts = new WP_Query($query);

これをNuxt.jsでやるとなると、不細工なコード含め2通り思いついたので、両方を検討していきます。

方法① 力業でPV順ソートする方法

力業です。 不細工なやり方です。 エレファントです。

REST APIにはカスタムフィールドでソートする機能がないっぽいので、REST APIから記事を全件取得し、Nuxt.js側でPV順に並べ替えます。

REST APIにはソートどころか、カスタムフィールド自体含まれていないので、カスタムフィールドは自前で追加します。

この方法の問題点

このサイトみたいに記事総数が50件程度ならなんとかなるでしょう。

APIが返すデータ量的にも、「context=embed」を指定したり、不要な要素を削除することでどうにかなりそうです。

ですが第1の壁。

記事数が100越えたらどないすんの?

WordPressのREST APIには全件取得の機能がないんです。

1回のリクエストで取得する記事数は「per_page」オプションで指定するんですが、許容範囲は1~100件。 それ以外を指定するとこんな↓エラーを吐きます。


{
  "code": "rest_invalid_param",
  "message": "無効なパラメーター: per_page",
  "data": {
    "status": 400,
    "params": {
      "per_page": "per_page は1以上、100以下でなければなりません。"
    }
  }
}

一応、複数回APIを叩くことで全件取得は可能です。

そして第2の壁。

記事数が1万とかになったらどないすんの?

記事数が200や300ならね、まだどうにかなります。

でも記事数が1万、2万とかなったら100回以上API叩かないといけないんですよ。

もう無理でしょこれ。

つまり、力業でのソートは限定的で、将来性考えるとダメです。 ゴミ。

方法② WordPress側でPV順でソートする

REST APIの仕様的にNuxt.js側でソートするのは無理がありそうなので、WordPress側で処理します。

これも方法はふたつありそうです。

独自APIでPV順にソートする

WordPressのREST APIには独自APIを追加する機能があります。

これを使うとWordPress上で出来ることなら何でもAPIにして渡せます。

カスタムフィールドでソートした記事一覧も返せますし、全件取得も1回のリクエストで済ますことができます。

でもね。

僕は独自APIのこの万能感というかワイルドカード感というかチート仕様というかが、どうにも気に食わないです。

なのでできれば違う方法で対応したい。

まぁそんなこと言ってみても独自API以外で実装不可能なら頼らないといけないわけでして。

いったん保留。

REST APIを改造してPV順でソートする

REST APIの範囲内でできました。

「rest_post_query」というアクションフックを使います。
(固定ページの場合は「rest_page_query」)

詳細はカスタムフィールドでソートの記事を見てもらうとして、実装したコードだけ。


add_action('rest_post_query', 'sortby_meta_key', 10, 2);
function sortby_meta_key($args, $request) {
  if ( isset($request['meta_key']) ) {
    $args['meta_key'] = $request['meta_key'];
    $args['orderby'] = 'meta_value_num';
    
    if ( isset($request['meta_orderby']) ) {
      $args['orderby'] = $request['meta_orderby'];
    }
  }
  return $args;
}

上記コードをfunctions.phpに追加すると、REST APIから記事検索する際に「meta_key」を指定できるようになり、ソートするカスタムフィールドを指定することができます。

あまり使うことはないと思いますが、ソート方法を「meta_value_num」と「meta_value」で切り替える場合は「meta_orderby」で指定します。
(デフォルトはmeta_value_num)

実際にREST APIからリクエストする場合はこの↓ように使います。


/wp/v2/posts?meta_key=views

これでカスタムフィールドでのソート=PV順でのソートが可能になります。

たぶんコピペで大丈夫です。 PV計測のプラグインが違う場合はカスタムフィールドの名前を変えてください。

Nuxt.js側の処理

人気記事はサイドバーやページ末尾など複数箇所で使うことがあると思うので、コンポーネントとして実装したいと思います。

<script>部分のコード


import axios from 'axios'

export default {
  data() {
    return {
      posts: [],
    }
  },
  mounted() {
    const query = 'https://xxxx.com/wp-json/wp/v2/posts' + 
                  '?context=embed' +
                  '&meta_key=views' +
                  '&order=desc' +
                  '&per_page=5'
    
    axios.get(query).then(res => {
      this.posts = res.data
    })
  },
}

「import axios」の部分はAPIと通信するための前処理です。 カテゴリページ作成の記事で導入手順から説明しているのでそちらを。

data return」の部分は変数宣言と考えてください。

「mounted」の部分はちょっと複雑で、通常APIリクエストをする際には「asyncData」や「fetch」の中で行うそうですが、今回はコンポーネント内です。 コンポーネント内ではasyncDataが使えない(実際に動きませんでした)ので「mounted」で処理しています。

「query」はREST APIの仕様プラス、先ほど実装した「meta_key」の組み合わせになっています。 上記クエリだと「カスタムフィールドviewsで降順に並べ替えた上位5件」を取得します。

「context=embed」はレスポンスを軽量化するオプションです。 記事APIはそのままだと記事本文まるまま入っているのでけっこうサイズが大きいです。

あとはaxiosでAPIを取得し、変数「posts」の中に人気記事上位5件を格納しています。

<template>部分のコード

シンプルに<ul>タグで並べるだけのコードです。


<ul>
<li v-for="post in posts">
<nuxt-link :to="'/post/' + post.slug + '/'">{{ post.title.rendered }}</nuxt-link>
</li>
</ul>

まず先ほど取得した「posts」。 これには記事APIの配列が入っていますが、それぞれの要素(post)は次のような構造になっています。


{
  "id": 101,
  "date": "2020-08-13T23:29:33",
  "slug": "wp-rest-slug",
  "type": "post",
  "link": "(略)",
  "title": {
    "rendered": "(略)"
  },
  "excerpt": {
    "rendered": "(略)",
    "protected": false
  },
  "author": 1,
  "featured_media": 0,
  "_links": { (略) }
}

なのでページタイトルは「post.title.rendered」、スラッグは「post.slug」で取得します。

これを前提に「v-for」という機能でループさせて記事一覧を出力しています。 他に「v-bind」というのも使っているので分からない場合はこの記事を見てください。

ページのURLは「post.link」に入っているにもかかわらず自分でスラッグから再設定しています。 これは今後WordPress側のパーマリンクを変更するようなことがあっても大丈夫なように、Nuxt.js側で再生成するようにしました。

この仕組みの問題点

えー、一見すごくうまくPV順ソートを実装できた感じがしますが、問題点もあるので書いておきます。

前日のPV、先週のPVでソートできない

これはたぶんWP-PostViewsの問題ですかね。

公開以降の累計PVしか集計していないので、もうちょっと感覚的にリアルな人気順でソートしたい場合に対応できません。

他のプラグインなら出来るやつがあるかもしれないし、ないかもしれない。

Nuxt.jsへ移行してからのPVがカウントされない

はい、これ、大問題です。

PVの集計をWordPress側に依存してしまったせいで、Nuxt.jsに移行した後はPVが一切増えていきません。

過去の記事ならなんとなく人気記事感のある並びになりますが、移行して以降の記事。

全くPVが増えません。

ダメですね、この方式は。

WordPress時代のPVを活かしつつ移行後もPVを加算できる方式か、前日とか先週とか、短いスパンでNuxt.js側で集計できる仕組みが必要だと思います。

オレが採用している方式

どっかのサイト作った時にこんな方式を作りました。

  • 毎日24時過ぎたらアナリティクスAPIからローカルプログラムで前日のPVを取得
  • PV取得後、このサイトのデータベースに前日PVを更新
  • WordPress側のCRONでデータベースの値をカスタムフィールドに更新

素人過ぎる僕がやっているのでたぶんめっちゃ無駄が多いです。

無駄が多いですが、やりたいことはやれていて、この方式だとNuxt.jsに移行後も問題なく使えます。

今、この方式に近い形でNuxt.jsに実装できないか検討しているので、実現すればそのコードを。 実現できなければ不細工極まる今の方式を公開したいと思います。

アナリティクスのReporting APIにはJavaScript版があるので、できそうな気がします。
(オレに出来るとは言っていない)

以上、WordPressからお届けしました!

シェアする: