TECH BOX

Technology blog from Web Engineer

この記事は最終更新日から4年以上経過しているため正確ではないかもしれません

GridsomeでWordPressの記事詳細ページに前後の記事リンクを追加する

GraphQLでWordPressの記事詳細を取得したときに、デフォルトでは前後の記事を取得するクエリは存在しません。
WordPressのAPIを自作で拡張すれば可能かもしれませんが、今回は管理画面側を触らずフロントエンド(Gridsome)側でのみ実装する方法を紹介します。

すべての前後記事リストを取得する

GraphQLでは前後の記事リストを取得することができます。

<page-query>
query Post ($path: String!) {
  # 記事詳細を取得
  wordPressPost (path: $path) {
    id
    # 〜〜中略〜〜
  }

  # すべての前後ページを取得
  allWordPressPost(perPage: 500) {
    edges {
      node {
        id
      }
      next {
        title
        path
      }
      previous {
        title
        path
      }
    }
  }
}
</page-query>

クエリは記事詳細(メイン)すべての前後ページの2つの大きなクエリで取得します。
すべての前後記事はこの時点ではメイン記事のIDがわからないので、IDでのフィルタができないため全ページ取得できるように取得数を多めに設定(ここでは500)にしておきます。

前後記事で必要なのは記事名と記事URL(制作方法によってはpathではなくslugでもよい)、記事IDが必要になります。
こうすると下記のようにデータが取得できます

{
  "data": {
    "wordPressPost": {
      "title": "GridsomeでStylusとPostCSSを使う",
      "id": "1099",
      ...
    },
    "allWordPressPost": {
      "edges": [
        {
          "node": {
            "id": "1102"
          },
          "next": {
            "title": "GridsomeでStylusとPostCSSを使う",
            "path": "/gridsome-stylus"
          },
          "previous": null
        },
        {
          "node": {
            "id": "1099"
          },
          "next": {
            "title": "GridsomeでTypeScriptを使う",
            "path": "/gridsome-ts"
          },
          "previous": {
            "title": "GridsomeでPugを使う",
            "path": "/gridsome-pug"
          }
        },
        {
          "node": {
            "id": "1091"
          },
          "next": null,
          "previous": {
            "title": "GridsomeでStylusとPostCSSを使う",
            "path": "/gridsome-stylus"
          }
        },
        ...
      ]
    }
  }
}

wordPressPost > idallWordPressPost > edges > node > idが記事のIDになるので一致する箇所を手動でフィルタリングすれば前後の記事が取得できます。

Vue側の前準備

これをVueのmountedメソッドで実行すればいいと思いがちですが、記事詳細から記事詳細へ遷移する場合にはmountedなどの自動実行メソッドは最初のアクセス以降は処理が走りません。
厳密には同一コンポーネント内の書き換えではキャッシュが効いているため処理されません。

なのでルーターをwatchして変更があるたびに処理をする方法にします。

export default {
  data() {
    return {
      newerPost: null,
      olderPost: null
    }
  },

  mounted() {
    this.init()
  },

  watch: {
    '$route'(to, from) {
      this.init()
    }
  },

  methods: {
    init() {
      this.$nextTick(() => {
        // 最初に実行する処理
      })
    }
  }
}

フィルタ処理を行う

フィルターというか記事IDが最初に一致するデータを取得すればいいので処理自体は簡単です。

init() {
  this.$nextTick(() => {
    const currentID = this.$page.wordPressPost.id
    const edges = this.$page.allWordPressPost.edges
    const edge = edges.find((e) => e.node.id === currentID)

    this.newerPost = edge.previous
    this.olderPost = edge.next
  })
}

ここで注意するのが取得したデータのpreviousが自身の記事より新しい日付の記事、nextが自身の記事より古い日付の記事になります。
個人的にprev/nextは解釈が人それぞれで分かりづらいので、newer/olderでわかりやすい変数に代入します。

あとはHTML側に好きなようなレイアウトではめればOKです。