ギフティ Advent Calendar 2019 - Qiitaの1日目の記事。このブログにタグ機能を導入したので、その時の手順を解説する。基本的に公式の記事に従えば良いのだが、差分が分かりづらいのと、自分で理解した内容を補足したいので、記事にした。
このブログはGatsby公式のgatsby-starter-blogをベースに作られている。
gatsbyjs/gatsby-starter-blog: Gatsby starter for creating a blog
この記事に付けられているタグ(「GatsbyJSで最強のブログを作る」)をクリックすると、そのタグが付けられた記事に絞り込まれた一覧を確認できる。
各記事のマークダウンファイルに下記のようにtagsを追加していく。
---
title: "GatsbyJSで作っているブログにタグ機能を導入した"
date: "2019-12-01 22:00"
tags: ["GatsbyJSで最強のブログを作る", "GatsbyJS"]---
## はじめに
この作業が終わった時点で、 http://localhost:8000/___graphql に行き、
{
allMarkdownRemark {
group(field: frontmatter___tags) {
tag: fieldValue
totalCount
}
}
}
というクエリを投げると、
{
"data": {
"allMarkdownRemark": {
"group": [
{
"tag": "CSS",
"totalCount": 2
},
{
"tag": "GatsbyJS",
"totalCount": 3
},
{
"tag": "Netlify",
"totalCount": 1
}
...
]
}
}
}
このような結果が得られる。タグ毎にグルーピングされた結果が取得できていることが分かる。このタグのグループを取得するクエリを上手く活用して、各タグの記事一覧ページを生成していく。
このブログで言う、下記のようなページだ。
https://kikunantoka.com/tags/gatsby-js/
各マークダウンファイルのtagsの値を読み込んで、tags/{tags} に各タグの記事一覧ページが生成されるように gatsby-node.js
ファイルを下記のように編集していく。ハイライトしている部分に注目して欲しい。
const path = require(`path`)
const _ = require("lodash")const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = ({ graphql, actions }) => {
const { createPage } = actions
const postTemplate = path.resolve(`./src/templates/posts.js`)
const tagTemplate = path.resolve(`./src/templates/tags.js`)
return graphql(
`
{
posts: allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
limit: 1000
) {
edges {
node {
fields {
slug
}
frontmatter {
title
}
}
}
}
tags: allMarkdownRemark(limit: 1000) { group(field: frontmatter___tags) { fieldValue } } }
`
).then(result => {
if (result.errors) {
throw result.errors
}
const posts = result.data.posts.edges
posts.forEach((post, index) => {
const previous = index === posts.length - 1 ? null : posts[index + 1].node
const next = index === 0 ? null : posts[index - 1].node
createPage({
path: post.node.fields.slug,
component: postTemplate,
context: {
slug: post.node.fields.slug,
previous,
next,
},
})
})
const tags = result.data.tags.group tags.forEach(tag => { createPage({ path: `/tags/${_.kebabCase(tag.fieldValue)}/`, component: tagTemplate, context: { tag: tag.fieldValue, }, }) })
return null
})
}
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
先ほど試したクエリを使って、タグ一覧を取ってきて、それらのタグ毎にテンプレートに当てはめたページを生成していく内容となっている。createPageのcomponentにタグ用のテンプレートを渡している点と、contextにそのタグ名を渡している点が注目点だ。
各タグの記事一覧ページ用のテンプレートを作る。先ほどの gatsby-node.js
で読み込んだパスと同じであれば、どこに配置しても良い。src/templates/
のようなフォルダを切ると良さそう。下記がこのブログの各タグの記事一覧ページ用のテンプレートだ。
import React from "react"
import Bio from "../components/bio"
import Layout from "../components/layout"
import Section from "../components/section"
import Post from "../components/post"
const Tags = ({ pageContext, data }) => {
return (
<Layout>
<Section>
<Bio />
</Section>
<h1>
{pageContext.tag} ({data.allMarkdownRemark.totalCount}件)
</h1>
{data.allMarkdownRemark.edges.map(({ node }) => {
return (
<Post
slug={node.fields.slug}
title={node.frontmatter.title}
date={node.frontmatter.date}
tags={node.frontmatter.tags}
/>
)
})}
</Layout>
)
}
export default Tags
export const pageQuery = graphql`
query($tag: String) { allMarkdownRemark( limit: 1000 sort: { fields: [frontmatter___date], order: DESC } filter: { frontmatter: { tags: { in: [$tag] } } } ) { totalCount edges { node { excerpt(truncate: true) fields { slug } frontmatter { title date tags } } } } }
createPageのcontextに渡したtagの内容を元にフィルタリングした記事一覧を取得して、その記事一覧を描画する内容になっている。記事のパラメータを渡すと記事へのリンクと概要を表示してくれるPostコンポーネントのようなものを用意しておくと、記事一覧ページとタグページで共通化ができて良い。
各タグへのリンクは、下記のサンプルのように gatsby-node.js
で記述したcreatePageのpathにリンクすると良い。こちらでも同様にlodashを活用する。
import _ from "lodash"
{tags.map(tag => {
return <Link to={`/tags/${_.kebabCase(tag)}/`}>{tag}</Link>
})}
記事ページ側で、titleやdateと同様にtagsも取得するようにクエリを修正した上で、上述のようなリンクを追加すれば、各記事にタグが表示され、そのタグをクリックするとクリックしたタグの記事一覧ページが表示されるようになる。あとはスタイルを整えれば、完成だ。以上で作業は終了となる。
記事を書こうとして、理解が深まったり、リファクタが進んだので良かった。