published on : 2019.10.17Web制作

#3 Gatsby + WordPress REST APIでブログを作ろう【カテゴリーページ編】

今回はGatsby連載の#3「カテゴリーページの作り方」編です^ ^

    STEPS
  • Step1:WordPress側で記事にカテゴリーを追加
  • Step2:記事のカテゴリーを全部取得する
  • Step3:カテゴリーページのテンプレートを作る
  • Step4:gatsby-node.jsをカテゴリーページを生成するように編集する
  • Step5:全てのカテゴリーの一覧を表示させるカテゴリーインデックスページを作成
  • Step6:それぞれの記事にカテゴリーを表示

Step1:WordPress側で記事にカテゴリーを追加

WordPressで使用したいカテゴリーを追加してテストでいくつかサンプル記事を準備しておきます

何も設定していないと「Uncategorized」or「未分類」になります

Step2:記事のカテゴリーを全部取得する

graphqlの画面でallWordpressCategoryで全てのカテゴリーがちゃんと取ってこれているか確認します

ちゃんと取れていますね^ ^

gatsby developをしたままですと更新されてないかと思いますので、一旦止めてgatsby developし直してください

*allWordpressPostの中にあるcategories{name}ではうまくいかなかったです

Step3:カテゴリーページのテンプレートを作る

templateの中にCategories.jsを作ります

これがカテゴリーページのテンプレートになります

Categories.js
import React from "react"
import { Link, graphql } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

const Categories = ({ data, pageContext }) => {
  const { category } = pageContext
  return (
    <Layout>
      <SEO title={category} keywords={category} />
      <div>
        <h2>{category}</h2>
        {data.allWordpressPost.edges.map(post => (
          <div key={post.node.id} className={"post"}>
            <Link to={`/${post.node.slug}`} >
              {post.node.featured_media &&
                <div>
                  <Img
                    fluid={post.node.featured_media.localFile.childImageSharp.fluid}
                    alt={post.node.title}
                  />
                </div>
              }
              <h3>{post.node.title}</h3>
            </Link>
            <div className={"post-content"} dangerouslySetInnerHTML={{__html: post.node.excerpt}} />
            {post.node.date}
          </div>
        ))}
      </div>
    </Layout>
  )
}
export default Categories

export const query = graphql`
  query ($category: String!) {
    allWordpressPost(filter: {categories: {elemMatch: {name: {eq: $category}}}}) {
      edges {
        node {
          id
          excerpt
          title
          slug
          featured_media {
            localFile {
              childImageSharp {
                fluid(maxWidth: 1000) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
          date(formatString: "MMMM DD, YYYY")
        }
      }
    }
  }
`

pageContextについては後ほどgatsby-node.jsの箇所で解説します

queryでは$category : String!で例えば”test”カテゴリが渡ってきて、そのカテゴリにマッチする記事を全部表示するようにフィルターにかけています

graphqlのページで見てみるとある程度わかるかと思いますが、query($category: String!)と書いてもちゃんと動いてくれないので、実際に書いてgatsby developをして確認するしか無いです

まだgatsby-node.jsを編集していないので、何もおきません

Step4:gatsby-node.jsをカテゴリーページを生成するように編集する

まずlodashを使いたいのでnpmかyarnでlodashをインストールします

Terminal
npm install --save lodash
or
yarn add lodash

gatsby-node.jsに追記していきます

gatsby-node.js
const path = require(`path`)
const _ = require(`lodash`) //<-lodashをrequire
const createPaginatedPages = require('gatsby-paginate')

exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions
  const BlogPostTemplate = path.resolve("./src/templates/BlogPost.js")
  const PageTemplate = path.resolve("./src/templates/Page.js")
  const CatTemplate = path.resolve("./src/templates/Categories.js") //<-カテゴリーページのテンプレート

  const result = await graphql(`
    {
      allWordpressPost {
        edges {
          node {
            id
            title
            date(formatString: "MMMM DD, YYYY")
            excerpt
            slug
            wordpress_id
            categories {
              name
            }
            featured_media {
              localFile {
                childImageSharp {
                  fluid(maxWidth: 1000) {
                    src
                    srcSet
                    aspectRatio
                    sizes
                    base64
                  }
                }
              }
            }
          }
        }
      }
      allWordpressPage {
        edges {
          node {
            slug
            wordpress_id
          }
        }
      }
      allWordpressCategory {
        edges {
          node {
            name
          }
        }
      }
    }
  `)
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  const BlogPosts = result.data.allWordpressPost.edges
  BlogPosts.forEach(post => {
    createPage({
      path: `/${post.node.slug}`,
      component: BlogPostTemplate,
      context: {
        id: post.node.wordpress_id,
      },
    })
  })

  const Pages = result.data.allWordpressPage.edges
    Pages.forEach(page => {
      createPage({
        path: `/${page.node.slug}`,
        component: PageTemplate,
        context: {
          id: page.node.wordpress_id,
        },
      })
    })

  createPaginatedPages({
    edges: BlogPosts,
    createPage: createPage,
    pageTemplate: 'src/templates/Pagination.js',
    pageLength: 5,
    pathPrefix: 'posts',
  })

  // 全カテゴリーの名前を取得しcatsの中に入れる
  const BlogCategories = result.data.allWordpressCategory.edges
  let cats = []
  _.each(BlogCategories, edge => {
    if (_.get(edge, "node.name")) {
      cats = cats.concat(edge.node.name)
    }
  })
  // 被ったカテゴリーを排除
  cats = _.uniq(cats)

  // 取ってきたカテゴリー毎にテンプレート(Categories.js)を使ってページを生成する
  cats.forEach(category => {
    createPage({
      path: `/categories/${_.kebabCase(category)}/`,
      component: CatTemplate,
      context: {
        category: category, //<-これがpageContextとしてCategories.jsに渡る、queryで($category: String!)が使えるようになる
      },
    })
  })
}

コメントで少し解説していますが、やっていることは超簡単に言うと「全カテゴリーを取得してそのカテゴリー毎にページをテンプレート(Categories.js)を使用して生成している」ということです

allWordpressPostとallWordpressPageと別にallWordpressCategoryを追加して全カテゴリーのデータを引っ張ってきています

categories/test/に行くとちゃんと”test”カテゴリーの記事が表示されるかと思います

allWordpressPost内のcategories{name}で引っ張ってきて同じことをしようとしたのですが、うまくいかなかったのでやめましたw


*追記:allWordpressPost内のcategories{name}からでもいけました!!w

~~categories.nameで取ろうとしていたのをcategories[0].nameに変えるとちゃんと取れるようになりました

consoleで引っ張ってきているデータをみると、categories.nameではなく、categories[0]の中にnameがあるのがわかります

なので書き換えてみましたw

gatsby-node.js
const path = require(`path`)
const _ = require(`lodash`)
const createPaginatedPages = require('gatsby-paginate')

exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions
  const BlogPostTemplate = path.resolve("./src/templates/BlogPost.js")
  const PageTemplate = path.resolve("./src/templates/Page.js")
  const CatTemplate = path.resolve("./src/templates/Categories.js")

  const result = await graphql(`
    {
      allWordpressPost {
        edges {
          node {
            id
            title
            date(formatString: "YYYY.MM.DD")
            excerpt
            slug
            wordpress_id
            categories {
              name
            }
            featured_media {
              localFile {
                childImageSharp {
                  fluid(maxWidth: 1000) {
                    src
                    srcSet
                    aspectRatio
                    sizes
                    base64
                  }
                }
              }
            }
          }
        }
      }
      allWordpressPage {
        edges {
          node {
            slug
            wordpress_id
          }
        }
      }
    }
  `)
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  const BlogPosts = result.data.allWordpressPost.edges
  BlogPosts.forEach(post => {
    createPage({
      path: `/${post.node.slug}`,
      component: BlogPostTemplate,
      context: {
        id: post.node.wordpress_id,
      },
    })
  })

  const Pages = result.data.allWordpressPage.edges
    Pages.forEach(page => {
      createPage({
        path: `/${page.node.slug}`,
        component: PageTemplate,
        context: {
          id: page.node.wordpress_id,
        },
      })
    })

  createPaginatedPages({
    edges: BlogPosts,
    createPage: createPage,
    pageTemplate: 'src/templates/Pagination.js',
    pageLength: 5,
    pathPrefix: 'posts',
  })

  const BlogCategories = result.data.allWordpressPost.edges
  let cats = []
  _.each(BlogCategories, edge => {
    if (_.get(edge, "node.categories[0].name")) {
      cats = cats.concat(edge.node.categories[0].name)
    }
  })
  cats = _.uniq(cats)

  cats.forEach(category => {
    createPage({
      path: `/categories/${_.kebabCase(category)}/`,
      component: CatTemplate,
      context: {
        category: category,
      },
    })
  })
}

これでもちゃんと表示されました!!

“uncategorized”もいけてます

gatsby buildコマンドをするとpublicディレクトリの中にcategoriesフォルダが生成されてその中に全カテゴリーが入っているのがわかるかと思います

これでカテゴリーページの作り方の基礎はわかったかと思います^ ^

ステップ5:全てのカテゴリーの一覧を表示させるカテゴリーインデックスページを作成
必要かどうかはさておき、categoriesにアクセスされた時に表示するカテゴリーの一覧ページを作ります

pagesディレクトリの中にcategories.jsを作るだけです

categories.js
import React from "react"
import { graphql, Link } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"

const CategoriesIndex = ({ data }) => (
  <Layout>
    <SEO title="Categories" keywords="カテゴリー一覧ページ" />
    <ul>
      {data.allWordpressCategory.edges.map(post => (
        <li key={post.node.id}>
          <Link to={`/categories/${post.node.slug}`} >
            {post.node.name}
          </Link>
        </li>
      ))}
    </ul>
  </Layout>
)

export default CategoriesIndex

export const query = graphql`
  query {
    allWordpressCategory {
      edges {
        node {
          id
          slug
          name
        }
      }
    }
  }
`

超シンプルですね

こんな感じでちゃんとカテゴリーが表示されているかと思います^ ^

Step6:それぞれの記事にカテゴリーを表示

いくつか例を挙げておきます

やっていることはqueryにcategories{name}を追加して、~~categories[0].nameで表示しているだけなのですんなり表示できるかと思います

pages/index.js
import React from "react"
import { graphql, Link } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

const IndexPage = ({ data }) => {
  return(
  <Layout>
    <SEO title="Home" keywords={[`gatsby`, `test`, `sample`]} />
    <ul>
      {data.allWordpressPost.edges.map(post => (
        <li key={post.node.id}>
          <Link to={`/${post.node.slug}`} >

            {post.node.featured_media && (
              <div>
                <Img
                  fluid={post.node.featured_media.localFile.childImageSharp.fluid}
                  alt={post.node.title}
                />
              </div>
            )}

            <div>
              <h3 dangerouslySetInnerHTML={{ __html: post.node.title }} />
              <span>{post.node.date}</span>
              <span>{post.node.categories[0].name}</span>
              <div dangerouslySetInnerHTML={{ __html: post.node.excerpt }} />
            </div>
          </Link>
        </li>
      ))}
    </ul>
  </Layout>
)
}

export default IndexPage

export const query = graphql`
  query {
    allWordpressPost(sort: {order: DESC, fields: date}) {
      edges {
        node {
          id
          title
          excerpt
          slug
          date(formatString: "YYYY.MM.DD")
          categories {
            name
          }
          featured_media {
            localFile {
              childImageSharp {
                fluid(maxWidth: 1000) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
        }
      }
    }
  }
`

これでトップページの記事の日付の横にカテゴリー名が表示されるかと思います

わかりにくいかもですが、日付の横にカテゴリー名の”test”と”uncategorized”が表示されました

記事ページにも表示させてみます

BlogPost.js
import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

const BlogPostTemplate = ({ data }) => (
  <Layout>
    <SEO
      title={data.wordpressPost.title}
      description={data.wordpressPost.excerpt}
    />
    <h1>{data.wordpressPost.title}</h1>
    <span>{data.wordpressPost.date}</span>
    <span>{data.wordpressPost.categories[0].name}</span>
    {data.wordpressPost.featured_media && (
      <div>
        <Img fluid={data.wordpressPost.featured_media.localFile.childImageSharp.fluid} />
      </div>
    )}
    <div
      dangerouslySetInnerHTML={{ __html: data.wordpressPost.content }}
    />
  </Layout>
)
export default BlogPostTemplate

export const query = graphql`
  query($id: Int!) {
    wordpressPost(wordpress_id: { eq: $id }) {
      title
      content
      excerpt
      date(formatString: "YYYY.MM.DD")
      categories {
        name
        slug
      }
      featured_media {
        localFile {
          childImageSharp {
            fluid(maxWidth: 1000) {
              ...GatsbyImageSharpFluid
            }
          }
        }
      }
    }
  }
`

これでこんな感じに表示されたかと思います

簡単ですね^ ^

お疲れ様でした!

これでカテゴリーページ編は終わりです

データを引っ張ってくる方法がわかりさえすれば、Gatsbyは非常に便利ですし簡単ですよね^ ^

次回は記事ページの「breadcrumbs」を追加していきます^ ^

では〜〜

Share if you like

Commentなんでも気軽にどうぞ^ ^

Author
Web Designer

Kentaro Koga

福岡を拠点のクリエイターチーム「Fump」として活動をしています。

写真や動画の撮影やモノ作りが好きで、趣味は旅・カメラ・テニス・筋トレ・サウナ・レザークラフトです。

twitterアイコンinstagramアイコンfacebookアイコン