LoginSignup
3
0

More than 3 years have passed since last update.

Gatsby.jsで作ったブログにArchiveページを作成する

Posted at

動機

静的サイトジェネレーターのGatsby.jsでブログを開発するにあたり、Archiveを作ろうと思い挑戦。
私が調べた(対して調べてない・・・)範囲ではArchive作る便利なpluginとかないないみたい。なので、実作。ベストプラクティスではない、と思う。

作り

記事を作成した月ごとにまとめるよくある感じにする。
まだ7、8月しか記事を書いてないので2ヶ月分しかない。もっと書かなきゃ。

環境

・OS: macOS 10.15.5
・gatsby: 2.24.14
・ヘッドレスCMS: Contentful
・FW: React Bootstrap

手順

1.templateフォルダにarchiveページ用のテンプレートを作成する。
2.gatsby-node.jsで、archiveページを生成する
3.archiveページのComponentを作成する。

まずはarchiveページ用のテンプレートページを作っていきます。テンプレートはページの構成が一緒で文章と画像を差し替えるだけでいいページをたくさん作る時に使います。便利。

archiveTemplate.js
import React from "react"
import Layout from "../components/layout"
import { graphql, Link } from "gatsby"
import { Container, Row, Col, Card, Button } from "react-bootstrap"
import Img from "gatsby-image"
import Archive from "../components/archive"

export const query = graphql`
  query($skip: Int!, $limit: Int!, $currentYearMonth: Date!, $nextYearMonth: Date!){
    allContentfulCode(
      sort: {fields: createdDate, order: DESC},
      limit: $limit,
      skip: $skip,
      filter:  {createdDate :
        {
          lt: $nextYearMonth,
          gte: $currentYearMonth
        }
      }
      ){
      edges {
        node {
          title
          slug
          category {
            category
            categorySlug
            id
          }
          description {
            content {
              content {
                value
              }
            }
          }
          createdDate(formatString: "YYYY/MM/DD")
          thumbnail{
            fluid(maxWidth: 500) {
              src
              ...GatsbyContentfulFluid_withWebp
            }
            description
          }
        }
      }
    }
  }
`

function ArchiveTemplate(props) {
  return (
    <Layout>
      <Container fluid >
        <Row className="justify-content-md-center">
          <Col xs={7} className="mt-3" style={{ borderBottom: "3px solid grey" }} >
            <h1 className="m-3">{`${props.pageContext.currentYearMonth}`}</h1>
          </Col>
          <Col xs={3} >
          </Col>
        </Row>
        <Row className="justify-content-md-center">
          <Col xs={7} style={{ padding: "20px", textAlign: "center" }} className="mt-3" >
            {props.data.allContentfulCode.edges.map((edge, index) => (
              <Card className="m-5">
                <Card.Body>
                  <Card.Text>
                    <Link to={`/code/${edge.node.category[0].categorySlug}/${edge.node.slug}`}>
                      <h3>{edge.node.title}</h3>
                    </Link>
                    {edge.node.category.map(x => (
                      <li className={x.categorySlug} key={x.id} style={{ listStyleType: "none" }}>
                        <Link to={`/code/${x.categorySlug}/`}>{x.category}</Link>
                      </li>
                    ))}
                  </Card.Text>
                </Card.Body>
                <figure>
                  <Link to={`/code/${edge.node.category[0].categorySlug}/${edge.node.slug}`}>
                    <Img
                      fluid={edge.node.thumbnail.fluid}
                      alt={edge.node.thumbnail.description}
                      style={{ height: "300px" }}
                    />
                  </Link>
                </figure>
                <Card.Body>
                  <Card.Text>
                    {edge.node.description.content[0].content[0].value}
                  </Card.Text>
                  <Button variant="outline-secondary" href={`/code/${edge.node.category[0].categorySlug}/${edge.node.slug}`}>See More</Button>
                </Card.Body>
                <Card.Footer className="text-muted">作成日 {edge.node.createdDate}</Card.Footer>
              </Card>
            ))}
            <div>
              {!props.pageContext.isFirst && (
                <div className="text-left" style={{ display: "inline" }}>
                  <Link
                    to={
                      props.pageContext.currentPage === 2
                        ? `/${props.pageContext.currentYear}/${props.pageContext.currentMonth}/`
                        : `/${props.pageContext.currentYear}/${props.pageContext.currentMonth}/${props.pageContext.currentPage - 1}`
                    }
                    rel='prev'>
                    <span className="m-5">前のページ </span>
                  </Link>
                </div>
              )}
              {!props.pageContext.isLast && (
                <div className="text-right" style={{ display: "inline" }}>
                  <Link to={`/${props.pageContext.currentYear}/${props.pageContext.currentMonth}/${props.pageContext.currentPage + 1}/`} rel='next'>
                    <span className="m-5">次のページ</span>
                  </Link>
                </div>
              )}
            </div>
          </Col>
          <Col xs={3}>
            <Archive />
          </Col>
        </Row>
      </Container>
    </Layout >
  )
}

export default ArchiveTemplate

ポイント

GraphQLのfilterを使って、データの絞り込みを行う。今回は対象の月のデータを、対象の月以上、翌月未満で絞り込んで取得。
filter: {createdDate :
{
lt: $nextYearMonth,
gte: $currentYearMonth
}
}

次にarchiveページを生成します。生成場所は、gatsby-node.jsです。
まずはqraphqlを使って、全データを取得します。項目は記事生成日のみ。

gatsby-node.js
const path = require('path')

module.exports.createPages = async ({ graphql, actions}) => {
  const { createPage } = actions
  const archiveTemplate = path.resolve('./src/template/archiveTemplate.js')
  const res = await graphql(`
    query{
      allContentfulData(sort: {fields: createdDate, order: DESC}) {
        edges {
          node {
            createdDate
          }
        }
      }
    }
  `)

次にarchiveページを生成していきます。

gatsby-node.js
// archive //
  const archivePerpage = 5 //1ページに表示する記事の数
  const yearMonth = ['2020-07', '2020-08', '2020-09', '2020-10', '2020-11', '2020-12'];
  const archiveByMonth = yearMonth.map(x => {
    const obj = {};
    obj[x] = (res.data.allContentfulData.edges.filter(y => y.node.createdDate.match(x))).length;
    return obj;
  });

  archiveByMonth.forEach((x) => {
    const archivePosts = x[Object.keys(x)] //記事の総数 
    if (archivePosts === 0) return;
    const archivePages = Math.ceil(archivePosts / archivePerpage) //記事一覧ページの総数
    const currentYear = Object.keys(x)[0].split('-')[0];
    const currentMonth = Object.keys(x)[0].split('-')[1];
    const nextMonth = Number(currentMonth) + 1 === 13 ? '01' : ('00' + String(Number(currentMonth) + 1)).slice(-2);
    const nextYear = (nextMonth === '01') ? String(Number(nextMonth) + 1) : currentYear;

    Array.from({ length: archivePages }).forEach((_, i) => {
      createPage({
        path:
          i === 0
            ? `/${currentYear}/${currentMonth}`
            : `/${currentYear}/${currentMonth}/${i + 1}/`,
        component: archiveTemplate,
        context: {
          currentYearMonth: currentYear + '-' + currentMonth,
          nextYearMonth: nextYear + '-' + nextMonth,
          currentYear,
          currentMonth,
          skip: archivePerpage * i,
          limit: archivePerpage,
          currentPage: i + 1, // 現在のページ番号
          isFirst: i + 1 === 1, //最初のページ
          isLast: i + 1 === archivePages, // 最後のページ
        }
      })
    })
  })

ポイント

月ごとにデータ数をカウントする。
const yearMonth = ['2020-07', '2020-08', '2020-09', '2020-10', '2020-11', '2020-12'];
  const archiveByMonth = yearMonth.map(x => {
const obj = {};
obj[x] = (res.data.allContentfulData.edges.filter(y => y.node.createdDate.match(x))).length;
return obj;

URLは/YYYY/MM/ページ番号にする。
? `/${currentYear}/${currentMonth}`
: `/${currentYear}/${currentMonth}/${i + 1}/`,

最後にarchiveのCompenentを作成する。

archive.js
import React from 'react';
import { useStaticQuery, graphql, Link } from 'gatsby'
import { Container, Row, Col } from 'react-bootstrap';
import Style from './layout.module.scss'

const Archive = (props) => {
    const datas = useStaticQuery(graphql`
    query{
        allContentfulCode {
            edges {
              node {
                createdDate(formatString:"YYYY/MM/DD")
               }
            }
        }
    }
`)
    const date = ['2020/07', '2020/08', '2020/09', '2020/10', '2020/11', '2020/12'];
    const count = date.map(x => {
        const obj = {};
        obj[x] = (datas.allContentfulCode.edges.filter(y => y.node.createdDate.match(x))).length;
        return obj;
    });
    return (
        <div className={Style.archive_wrap}>
            <Container className={Style.content}>
                <Row>
                    <Col>
                        <h3>Archive</h3>
                        <ui>
                            {count.map((x, index) => {
                                if (x[Object.keys(x)] === 0) return false;
                                return <li key={index}><Link to={`/${Object.keys(x)}`}>{`${Object.keys(x) + ' (' + x[Object.keys(x)] + ')'}`} </Link> </li>
                            }
                            )}
                        </ui>
                    </Col>
                </Row>
            </Container>
        </div>
    );
}

export default Archive

ポイント

記事数が0の月はarchiveリストに表示しない。
if (x[Object.keys(x)] === 0) return false;

完成

3
0
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
3
0