Craft CMS と Gatsby をつかった静的ファイル生成:タグアーカイブの生成 #craftcms

これは Craft CMS Advent Calendar 2020 7日目の記事です。

詳細ページを作ったのでタグアーカイブを作成してみる。

タグのリストを表示する

トップページにタグのリストを表示して、 tags/{slug} にリンクされるようにしてみる。

src/pages/index.js

import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import { Link } from "gatsby"

const RecentEntriesQuery = graphql`
  {
    allCraftTestTestEntry(limit: 10) {
      nodes {
        id
        title
        url
        slug
        ... on Craft_test_test_Entry{
          testredactor
          testtags{
            id
            title
          }
        }
      }
    }
    allCraftTags1Tag {
        tagList: nodes {
          id
          title
        }
      }
  }
`;

export default function Home() {
  const {allCraftTestTestEntry: {nodes}, allCraftTags1Tag:{tagList}} = useStaticQuery(RecentEntriesQuery);
  return (
    <div>
        <h1>Hello world!</h1>
        <div className="flex flex-wrap -mx-4">
            {nodes.map(({id, title,url,slug}) => (
                <p><Link to={`/article/${slug}`}>{title}</Link></p>
            ))}
        </div>
        <h2>タグリスト</h2>
        <ul className="flex flex-wrap -mx-4">
            {tagList.map(({id, title}) => (
                <li><Link to={`/tags/${title}`}>{title}</Link></li>
            ))}
        </ul>
    </div>
  )
}

タグの一覧を取得する allCraftTags1Tag を追加する。

all は一覧をとるということで
Craft は決まり事みたいなもんで
Tags1 はタググループのハンドル
Tag はタグを取得するという意味になる

Fetching Craft Content : Gatsby source plugin for Craft CMS
https://github.com/craftcms/ga...

タグの一覧が無事取得できた。

20201205 1521

タグアーカイブページを出力する

リンク先のタグアーカイブページを作成していく。gatsby-node.js を編集する。

gatsby-node.js

const path = require('path');

exports.createPages = ({ graphql, actions }) => {
    const {createPage} = actions
    return graphql(`
    {
        allCraftTestTestEntry(limit: 10) {
          nodes {
            id
            title
            url
            slug
            ... on Craft_test_test_Entry{
              testredactor
              testtags{
                id
                title
              }
            }
          }
        }
        allCraftTags1Tag {
            tagList: nodes {
              id
              title
            }
          }
    }
  `).then(result => {
        const allEntries = result.data.allCraftTestTestEntry.nodes
        allEntries.forEach(node => {
            createPage({
                path: `article/${node.slug}`,
                component: path.resolve(`./src/templates/post.js`),
                context: {
                    pagedata: node
                },
            })
        })
        const tagArchive = result.data.allCraftTags1Tag.tagList
        tagArchive.forEach( tag => {
            createPage({
                path: `tags/${tag.title}`,
                component: path.resolve(`./src/templates/tagarchive.js`),
                context: {
                    tagdata: tag,
                    tagId: tag.id,
                    allEntries: allEntries
                },
            })
        })
    })
}

にも同じように

allCraftTags1Tag {
            tagList: nodes {
              id
              title
            }
          }

を追加しておく。

受け取ったレスポンスを使ってタグアーカイブページを生成する。

const tagArchive = result.data.allCraftTags1Tag.tagList
        tagArchive.forEach( tag => {
            createPage({
                path: `tags/${tag.title}`,
                component: path.resolve(`./src/templates/tagarchive.js`),
                context: {
                    tagdata: tag,
                    tagId: tag.id,
                    allEntries: allEntries
                },
            })
        })

タグアーカイブページの中身のエントリも allEntries で渡しておいてそこからフィルタかけた方がいいのかな、、、という気がしつつ、スキル不足でそこは出来なかったので後日の課題。
毎回全てのエントリがきっかけになると無駄そうな気もするが。

ビルドする時間とかはこの辺をちゃんと見ておこう。

タグアーカイブ内の記事リストの表示

タグアーカイブの中を作成していく。

src/templates/tagarchive.js

import React from "react"
import {Link} from "gatsby";

const TagArchive = ({ pageContext, data:{tagarchive: {nodes}} }) => {
    const { tagdata, allEntries } = pageContext

    return(
        <div>
            <h1>タグ「{tagdata.title}」の記事一覧:{tagdata.id}</h1>
            <h2>タグがついた一覧</h2>
            {nodes.map(({id,slug,title,testtags}) => (
                <p><Link to={`/article/${slug}`}>{title}</Link>:: タグ
                    {testtags.map((tag) => (
                        <div>title: {tag.title} / {tag.id}</div>
                ))}</p>
            ))}
        </div>
    )
}
export default TagArchive

export const query = graphql`
  query ($tagId : String){
    tagarchive: allCraftTestTestEntry(limit: 10, filter: {testtags: {elemMatch: {id: {eq: $tagId}}}}) {
        nodes {
          id
          title
          url
          ... on Craft_test_test_Entry {
            testredactor
            testtags {
              id
              title
            }
          }
        }
    }
  }
`;

渡ってきた pageContext を受け取っておきつつ、このページで取得した tagarchive のクエリの内容を使って一覧を出力する。
この部分がページ生成と一緒にやったらいいんじゃないか、という気がするが。。。
このあたり色々理解が出来ていない。

gatsby-node.js で tagId: tag.id, を渡しておいて、このタグアーカイブのページでそれをそのまま利用する。

Gatsby Helper の方なのか gatsby-source-craft の話なのかはわからないが。
tag の id としてかえってくるのが tags1_Tag:200:1 こういうのだった。
tags1 はタググループのハンドルだと思うし、 _Tag はタグってことで、 200 がID。
 :1 はよくわからん・・・
この辺はプラグイン周りがアップデートあると変わりそうなきはする。

id としてうけとってそのまま渡せば問題ないのでひとまずは問題なし。

タグでフィルタをかけて取得するところは

export const query = graphql`
  query ($tagId : String){
    tagarchive: allCraftTestTestEntry(limit: 10, filter: {testtags: {elemMatch: {id: {eq: $tagId}}}}) {
        nodes {
          id
          title
          url
          ... on Craft_test_test_Entry {
            testredactor
            testtags {
              id
              title
            }
          }
        }
    }
  }
`;

こんなかんじで

allCraftTestTestEntry(limit: 10, filter: {testtags: {elemMatch: {id: {eq: $tagId}}}}

elemMatch でフィルタをかけてやると問題なくとれた。

ちなみに Craft CMS 上の GraphiQLでやるときはこんな感じなので

{
  tags(group: "tags1", id: "200") {
    id
    title
  }
}

取り方も違うので気をつける。

20201205 1543

$tagId は ID で宣言しておかないといけないかと思ったが String でよかったらしい。

tagarchive で受け取った結果をループしてページを表示する。

20201205 1544

最初にも書いたけど、gatsby-node.js での Query を元にタグアーカイブは作れないものかはまた今度試してみよう。