タグ機能、カテゴリ機能をつける(応用編)

Programming

はじめに

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

内容

前回はGraphQLを使用して、データを取得したJsonデータをTemplateに当てはめて画面に表示させるところまで行いました。 データの流れを理解できたでしょうか?この流れを理解するとあとは簡単です。

今回は応用編です。

「実際にはタグだけのページって必要でしょうか?」私は必要ないと思います。できれば、ユーザとしては選んだタグと記事の一覧が見れたら使いやすいはずです。

こんなタグ一覧+記事一覧ページを作るところをゴールに目指しましょう。

まずはタグ一覧+記事一覧のテンプレートファイルの作成

最初にGraphQLのクエリーを完成させよう

ポイントになってくるのがquery TagsListTemplate($tag: String!)tags: { glob: $tag }のTemplateの引数の指定の仕方と使い方です。globはglobalの略でいわゆる何でも検索ができます。*を入れたらすべてのタグが見つかるようになります。

タグ一覧(/tags)では全タグを表示させたいので*が入ってきますね。

query TagsListTemplate($tag: String!) {
    allMarkdownRemark(
        filter: { 
            frontmatter: { 
                template: { eq: "post" },
                draft: { ne: true }, 
                tags: { glob: $tag } } 
        },
        sort: { order: DESC, fields: [frontmatter___date] }
    ){
        group(field: frontmatter___tags) {
            fieldValue
            totalCount
        }
        edges {
            node {
                fields {
                    slug
                    categorySlug
                }
                frontmatter {
                    title
                    date
                    category
                    description
                    socialImage
                }
                excerpt
            }
        }
    }
}

あとは、記事を取得するクエリーとタグを取得するクエリーを記述しましょう。

group(field: frontmatter___tags) {
    fieldValue
    totalCount
}
edges {
    node {
        fields {
            slug
            categorySlug
        }
        frontmatter {
            title
            date
            category
            description
            socialImage
        }
        excerpt
    }
}

GraphQLを実際に http://localhost:8000/___graphql で試してJsonが取得できたら次へ進みましょう。

次は、取得したJsonをComponentに当てはめていきます。

const TagsListTemplate = ({ data, pageContext }) => {
  const tags = data.allMarkdownRemark.group;
  const posts = data.allMarkdownRemark.edges;

  return (
    <div className={TagsList}>
      <Tags tags={tags} selectedTag={pageContext.tag}/>
      <Post posts={posts} /> // それぞれの記事を表示するコンポーネントに合わせて差し替えてください
    </div>
  );
};

export const query = graphql`
    query TagsListTemplate($tag: String!) {
        allMarkdownRemark(
            filter: { frontmatter: { template: { eq: "post" }, draft: { ne: true }, tags: { glob: $tag } } },
            sort: { order: DESC, fields: [frontmatter___date] }
        ){
            group(field: frontmatter___tags) {
                fieldValue
                totalCount
            }
            edges {
                node {
                    fields {
                        slug
                        categorySlug
                    }
                    frontmatter {
                        title
                        date
                        category
                        description
                        socialImage
                    }
                    excerpt
                }
            }
        }
    }
`;

export default TagsListTemplate;

ここまででGraphQLクエリーでタグと記事の一覧を取得するクエリーを書き、Componentに当てはめることまでしました。次はタグのコンポーネントの作成です。

タグ一覧を表示するコンポーネントパーツの作成

先程作成したTemplateから呼び出されるタグコンポーネントです。こちらの例ではカウント順にソートしていますが、してもしなくてもOKです。

また、引数にselectedTagがありますが、選択している状態を表現したかったので表示しています。好みで変更してOKです。

const sortTotalCount = (tags) => orderBy(tags, ['totalCount', 'fieldValue'], ['desc']);

const Tags = ({ tags, selectedTag }: Props) => (
  <div className={styles["Tags"]}>
    {sortTotalCount(tags).map(tag => (
      <li key={tag.fieldValue}>
        <Link to={`/tags/${kebabCase(tag.fieldValue)}/`} className={selectedTag === tag.fieldValue ? styles['Tags--Selected'] : '' }>
          {tag.fieldValue}
          <span>{tag.totalCount}</span>
        </Link>
      </li>
    ))}
  </div>
);

export default Tags;

gatsby-node.js に /tags//tags/{tags} のルーティングを作成する

gatsby-node.js

まずはタグ一覧ページを登録します。

すべてのタグを表示するのでcontextに{ tag: ”*” }を指定しています。

  // Tags list
  createPage({
    path: '/tags',
    component: path.resolve('./src/templates/tags-list-template.js'),
    context: { tag: "*" }
  });

タグが選ばれた場合のURLを登録していきます。

ポイントはcontextの{ tag: tag.fieldValue }を指定している箇所です。これでURLが/tags/{tags}だったときにタグ名がTemplateに引数として受け渡しされることになります。

  const result = await graphql(`
    {
      allMarkdownRemark(
        filter: { frontmatter: { template: { eq: "post" }, draft: { ne: true } } }
      ) {
        group(field: frontmatter___tags) {
          fieldValue
          totalCount
        }
      }
    }
  `);

  _.each(result.data.allMarkdownRemark.group, (tag) => {
    const numPages = Math.ceil(tag.totalCount / postsPerPage);
    const tagSlug = `/tags/${_.kebabCase(tag.fieldValue)}`;

    for (let i = 0; i < numPages; i += 1) {
      createPage({
        path: i === 0 ? tagSlug : `${tagSlug}/page/${i}`,
        component: path.resolve('./src/templates/tags-list-template.js'),
        context: {
          tag: tag.fieldValue
        }
      });
    }
  }

処理の流れを表すとこのような図になります。

gatsby build を再度実行

/tags のページに遷移してみましょう。タグ一覧とその記事が表示されましたか?

まとめ

いかがだったでしょうか?うまく設定できたでしょうか?ここまで理解できたら他の実装にも応用できますね。それでは次回の記事で。

#CMS#Gatsby#GraphQL#Tags

Related Links


Gatsbyでブログを始めるまで
GatsbyにShare機能、OGPタグをつける
Gatsbyにタグ機能、カテゴリ機能をつける(基礎編)
GatsbyにTableOfContents(目次)をつける
DarkModeのCSSを適用させる
GatsbyブログのSEO対策でやっておくこと一覧
多機能なGatsbyJSのThemeをつくってnpmに公開した話
Yoshiki Ohashi
2x歳の個人事業主エンジニア。SI企業1年, Webベンチャー企業2年で上流から下流の経験を経て独立。 エンジニアらしく性格は温和。プロジェクトチームに心理的安全性を求める。go gin | Spring | Java | Kotlin | Vue | Python | 筋トレ | キャンプ | 個人開発 | 新潟出身

よく読まれている記事


Gatsbyでブログを始めるまで
GatsbyにShare機能、OGPタグをつける
Gatsbyにタグ機能、カテゴリ機能をつける(基礎編)
DL実装するときに理解すること
多機能なGatsbyJSのThemeをつくってnpmに公開した話
© 2020 Yoshiki Ohashi All rights reserved.