Contentful+Gatsbyアプリで記事のタグを表示するまで。
CMSとしてContentfulを使って構築したアプリで、各記事にタグを付けたくなるケースがあると思います。
こういうやつ。
Contentfulでは各記事のタイトルやURLスラッグも自分でフィールドを定義する必要があり(いわばすべてWordpressのカスタムフィールドみたいなもの)、タグ付のような動的にキーワードを関連づける機能がないので、Gatsbyアプリで自作してみようと思います。
前提
- Contentfulの会員登録が完了していて、すでに一般的な記事のContent modelが存在する
- アプリ側をGatsby.jsで構築している
ざっくりとしたやり方
今回Content上では、タグを記事と同列のコンテンツ(Content)として扱う方法を採ります。
どちらもContentfulにContent modelとして登録します。Content同士(記事とタグ)はそれぞれ多対多で紐づけることができるので、その機能を利用してタグ付けを行う方針です。
Contentfulのレコード数を消費するちょっと面倒な方法に見えますが、タグごとの記事一覧ページを作成する際など、タグ自体にメタ情報(Slugや別名、画像など)が必要になるケースで柔軟に対応ができるようになります。また複数の記事執筆者にタグを新規追加してもらいたいようなケースにも最適です。
Contentful側の準備
Contentful側でTagのContent modelを作成、つまりTagを一記事のように扱えるようにします。
タグモデルの作成
まず上部メニュー「Content model」から「Tags」という名前のContent modelを作成します。
とりあえず必要なフィールドは下記2つ。
- Title: タグ名として利用。「Text」タイプ(Short text)で登録
- Slug: タグのページを生成する際のURLスラッグに利用。「Text」タイプ(Short text)で登録
また、「Title」と「Slug」どちらもConfigureにて、「Required field」「Unique field」両方にチェックを入れてください(タグ一覧ページのURLがそれぞれ被らないようにするため)。
TagsのContent modelが下記のようになればOKです。
記事モデルの修正
次に、記事側でタグを紐づけられるフィールドを新規追加します。
Content fieldを新規作成し、「Rerference」タイプを選択すると、別のコンテンツを紐づけることが可能になります。
「Many references」を選択して、複数のTagを紐づけられるようにします。
「Validation」の設定にて、特定のContentのみを紐づけられるようにします。ここでは先ほど作成したContent modelの「Tags」を選択します。
これで記事モデル側の準備も完了です。下記のように、タグを紐づけたい記事のContent modelの下部にTagsが追加されていればOKです。
これで記事にタグを紐づけられるので、Contentfulの記事編集画面に遷移して、試しにタグ付けしてみるとこんな感じになります。
CMS上で複数のタグの紐付け、新規作成も簡単にできるようになりました。
Gatsby側の実装
まずGraphQLのクエリは下記のにします。
export const query = graphql`
query BlogArticleQueryTop {
allContentfulBlogArticle(filter: {node_locale: {eq: "ja-JP"}}) {
edges {
node {
id
title
slug
content {
content
}
thumbnail {
file {
url
}
}
tags {
title
slug
}
createdAt
}
}
}
}
`;
Contentfulのlocales設定で複数の言語を許容している場合のために、上記の例ではフィルタリング(filter: {node_locale: {eq: "ja-JP"}})
を行って、記事が重複して取得されない様にしています。
Tagsの要素としてtitle(タグ表記名)とslug(タグのURL用)を取得しています。
で、記事表示部分。今回は記事の一覧ページでの表示を想定しているので、map()関数で記事の配列を展開していて、その中でさらにTagsの配列を展開する形にしています。
const PostBasic = ({ postData }) => (
postData.map(({ node: post }) => (
<div className="post-basic-item">
<img src={post.thumbnail.file.url} alt="Slide1" className="thumbnail" />
<div className="post-basic-textblock">
<p className="post-basic-postedat">{post.createdAt}</p>
<h3><Link to={`/post/${post.slug}`}>{post.title}</Link></h3>
<p>{post.content.content}</p>
<div className="post-basic-catbox">
<span className="post-basic-catname">{post.category}</span>
<ul className="post-basic-tags">
{post.tags && post.tags.map(({ title, slug }) =>
<li>{title}: {slug}</li>
)
}
</ul>
</div>
</div>
</div>
))
)
上記コードの中でタグ表示部分を抜き出してみます。
{post.tags && post.tags.map(({ title, slug }) =>
<li><Link to={`/post/${slug}`}>#{title}</Link></li>
)
}
記事によってはタグを持たないケースがあるので、論理演算子( aaa && bbb
)によって、要素(この場合はpost.tagsを指す)がnullだったら配列の展開処理(map)を実行しないようにしています。基本的なことなのですが、最初は記事がタグを持たないケースがあることに気がつかずハマってしまいました・・・。
フロントでは下記のようになります。成功!
最後に
ContentfulはCMS側のセキュリティの気苦労を大幅に減らしてくれ、他のヘッドレスCMSと比較してカスタマイズ性も高い一方、タグ付、人気記事、関連記事などの機能は自前で実装する必要があります。
まだまだ日本語記事が少ない印象なので、今後もマイペースにブログで更新していきます。