16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Gatsbyにタグ機能、カテゴリ機能をつける(基礎編)

Last updated at Posted at 2020-05-28

はじめに

本記事は https://tech-blog.yoshikiohashi.dev/posts/start-gatsby-blog-add-tags のクロスポスト記事になります。

この記事はGatsbyというヘッドレスCMS技術で構成されています。今回は「エンジニア初心者でもできる」を前提に以下の構成で記事を作成していこうと思います。

内容

今回はWordPressのタグ機能、カテゴリ機能に当たる部分を実装していきます。標準で実装されてないの?!って突っ込みはあるかと思いますが、そうです。標準のテンプレートでは実装されておりません。なので実装するよりも最初からあるテンプレートを選んだほうが良いです。

テンプレートのおすすめはこちら

実装する前に

まずこちらの記事を読みましょう。GatsbyJS本家でタグの付け方について記載されています。

手順

  1. markdownファイルにタグを追加する
  2. GraphQLクエリを作成し、全てのタグを取得する
  3. タグページテンプレートを作成する(/tag/{tag}
  4. 作成したテンプレートを使用して、gatsby-node.jsでページをレンダリングする
  5. すべてのタグのリストを表示するタグインデックスページを作成する(/tags

1.markdownファイルにタグを追加する

タグがない場合のマークダウンファイル。ない場合追加する必要があります。

---
title: "A Trip To the Zoo"
---

I went to the zoo today. It was terrible.

tagsという名前で項目を追加しました。タグは複数個設定される想定なので、配列として定義します。他にも、文字列、数値が設定できます。(カテゴリも一緒で単一のため文字列として設定します。)

---
title: "A Trip To the Zoo"
tags: ["animals", "Chicago", "zoos"]
---

I went to the zoo today. It was terrible.

ローカル環境でgatsby developが実行されている場合は、再起動すると、Gatsbyが新しい項目を取得できるようになります。

2. GraphQLクエリを作成し、全てのタグを取得する

GraphQLを確認するためにはローカルでgatsby developを実行し、**http://localhost:8000/___graphql**にアクセスします。

下のように画面が見えるはずです。

画面が表示されたら下のクエリーを入力してみましょう。

{
  allMarkdownRemark {
    group(field: frontmatter___tags) {
      tag: fieldValue
      totalCount
    }
  }
}

タグ一覧が取得できるはずです。

ちなみにgroup()はSQLのGroupByと同じような意味合いです。ここではタグ項目でグルーピングしています。

3. タグページテンプレートを作成する(/tag/{tag}

ディレクトリはsrc/template/tags.jsなどの配置にしましょう。

import React from "react"
import PropTypes from "prop-types"

// Components
import { Link, graphql } from "gatsby"

const Tags = ({ pageContext, data }) => {
  const { tag } = pageContext
  const { edges, totalCount } = data.allMarkdownRemark
  const tagHeader = `${totalCount} post${
    totalCount === 1 ? "" : "s"
  } tagged with "${tag}"`

  return (
    <div>
      <h1>{tagHeader}</h1>
      <ul>
        {edges.map(({ node }) => {
          const { slug } = node.fields
          const { title } = node.frontmatter
          return (
            <li key={slug}>
              <Link to={slug}>{title}</Link>
            </li>
          )
        })}
      </ul>
      {/*
              This links to a page that does not yet exist.
              You'll come back to it!
            */}
      <Link to="/tags">All tags</Link>
    </div>
  )
}

Tags.propTypes = {
  pageContext: PropTypes.shape({
    tag: PropTypes.string.isRequired,
  }),
  data: PropTypes.shape({
    allMarkdownRemark: PropTypes.shape({
      totalCount: PropTypes.number.isRequired,
      edges: PropTypes.arrayOf(
        PropTypes.shape({
          node: PropTypes.shape({
            frontmatter: PropTypes.shape({
              title: PropTypes.string.isRequired,
            }),
            fields: PropTypes.shape({
              slug: PropTypes.string.isRequired,
            }),
          }),
        }).isRequired
      ),
    }),
  }),
}

export default Tags

export const pageQuery = graphql`
  query($tag: String) {
    allMarkdownRemark(
      limit: 2000
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { tags: { in: [$tag] } } }
    ) {
      totalCount
      edges {
        node {
          fields {
            slug
          }
          frontmatter {
            title
          }
        }
      }
    }
  }
`

補足

基本的に**src/template/**の配下のJSファイルはGraphQLがセットになっている構成が望ましいです。(必要でなければなくても良いです。)

流れで表すと下のようなイメージです。

GraphQL == クエリ結果 => Template == クエリ結果 => Componentの各パーツ

4. 作成したテンプレートを使用して、gatsby-node.jsでページをレンダリングする

さて、テンプレートページは完成したのであとはGatsby buildをするときにタグページを読み込ませるだけです。Gatsbyは最初に指定URLのページを読み込ませてビルドすることで静的なページが作成されていきます。

ここのgatsby-node.jssrc/templates/tags.jsに対してGraphQLで取得した結果をfor文でタグ数分生成しています。

const path = require("path")
const _ = require("lodash")

exports.createPages = async ({ actions, graphql, reporter }) => {
  const { createPage } = actions

  const blogPostTemplate = path.resolve("src/templates/blog.js")
  const tagTemplate = path.resolve("src/templates/tags.js")

  const result = await graphql(`
    {
      postsRemark: allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 2000
      ) {
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              tags
            }
          }
        }
      }
      tagsGroup: allMarkdownRemark(limit: 2000) {
        group(field: frontmatter___tags) {
          fieldValue
        }
      }
    }
  `)

  // handle errors
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  const posts = result.data.postsRemark.edges

  // Create post detail pages
  posts.forEach(({ node }) => {
    createPage({
      path: node.fields.slug,
      component: blogPostTemplate,
    })
  })

  // Extract tag data from query
  const tags = result.data.tagsGroup.group

  // Make tag pages
  tags.forEach(tag => {
    createPage({
      path: `/tags/${_.kebabCase(tag.fieldValue)}/`,
      component: tagTemplate,
      context: {
        tag: tag.fieldValue,
      },
    })
  })
}

5. すべてのタグのリストを表示するタグインデックスページを作成する(/tags

今度はタグ一覧ページを作成していきます。前に書いたGraphQLクエリーでタグ一覧を取得し、Templateに取得結果を当てはめていきます。

import React from "react"
import PropTypes from "prop-types"

// Utilities
import kebabCase from "lodash/kebabCase"

// Components
import { Helmet } from "react-helmet"
import { Link, graphql } from "gatsby"

const TagsPage = ({
  data: {
    allMarkdownRemark: { group },
    site: {
      siteMetadata: { title },
    },
  },
}) => (
  <div>
    <Helmet title={title} />
    <div>
      <h1>Tags</h1>
      <ul>
        {group.map(tag => (
          <li key={tag.fieldValue}>
            <Link to={`/tags/${kebabCase(tag.fieldValue)}/`}>
              {tag.fieldValue} ({tag.totalCount})
            </Link>
          </li>
        ))}
      </ul>
    </div>
  </div>
)

TagsPage.propTypes = {
  data: PropTypes.shape({
    allMarkdownRemark: PropTypes.shape({
      group: PropTypes.arrayOf(
        PropTypes.shape({
          fieldValue: PropTypes.string.isRequired,
          totalCount: PropTypes.number.isRequired,
        }).isRequired
      ),
    }),
    site: PropTypes.shape({
      siteMetadata: PropTypes.shape({
        title: PropTypes.string.isRequired,
      }),
    }),
  }),
}

export default TagsPage

export const pageQuery = graphql`
  query {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(limit: 2000) {
      group(field: frontmatter___tags) {
        fieldValue
        totalCount
      }
    }
  }
`

カテゴリーの実装

カテゴリー機能も考え方はタグと一緒です。タグは1つの記事に対し複数個の配列で構成され、カテゴリーは1記事に対して、単一の項目なので文字列としてマークダウンファイルに記述する必要があります。

実装手順

  1. markdownファイルに文字列型のカテゴリーを追加する
  2. GraphQLクエリを作成し、全てのカテゴリーを取得する
  3. カテゴリーページテンプレートを作成する
  4. 作成したテンプレートを使用して、gatsby-node.jsでページをレンダリングする
  5. すべてのカテゴリーのリストを表示するインデックスページを作成する

実装内容はほぼ一緒なので割愛します

まとめ

いかがだったでしょうか?ちょっと今回は難しかったでしょうか?このブログのソースコードは公開されているので良ければ参考にどうぞ。それでは次回の記事で。

https://github.com/yoshiki-0428/engneer-blog/blob/master/src/templates/tags-list-template.js#L29-L53

16
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?