Contentfulで、あるEntryへのLinkの数をどうやって取得すればいいのか分からないかもしれません。つまり、いくつかのContent Modelから参照している合計の数を取得する方法です。
Content Delivery APIには、Entriesを取得するための検索パラメータとしてlinks_to_entryというパラメータがあります。これを使っていきます。
本サイト「applis」はNext.js+Contentfulで作っていて、記事や単語、本といったコンテンツからタグを参照しています。このタグに紐づいたEntryの数を取得するのにlinks_to_entryを使っています。本サイトの実装を例に説明していきます。
テクニカルライター。元エンジニア。共著で「現場で使えるRuby on Rails 5」を書きました。プログラミング教室を作るのが目標です。
動作環境
この記事の内容は、次の各バージョンで動いています。本サイトはTypeScriptで書いているので、コードの例もTypeScriptとなっています。
| ライブラリ | バージョン |
|---|---|
| typescript | 4.1.5 |
| contentful.js | 8.1.7 |
links_to_entryとは
まずlinks_to_entryについてまとめます。これはContent Delivery APIでEntriesに対する検索パラメータのひとつです。ドキュメントには次のように書かれています。
あるエントリにリンクするフィールドをもつエントリを検索するには、links_to_entry という検索パラメータを用います。値にはエントリのIDを設定します。
ドキュメントのコード例そのままですが、JavaScriptの場合は次のようにして取得します。
client
.getEntries({
links_to_entry: '<entry_id>',
})
.then((response) => console.log(response.items))
.catch(console.error)
今回の例のように、複数のContent Modelから参照しているエントリについてはlinks_to_entryは有用です。ただ、特定のContent Modelから参照しているエントリを取得するときは、links_to_entryを使わずContent Typeを指定して取得する方が、パフォーマンスが高くなります。
links_to_entryの実装例
では実装例の方を見ていきます。本サイトのようにタグがあり、タグを参照する記事、単語、本という3つのContent Modelがあるアプリケーションを想定します。
Content Model
それぞれ、次のような構造であると仮定します。単語と本は記事とだいたい同じ構造なので省略します。
| Content Model | Field Name | Field Type |
|---|---|---|
| Tag | Name | Short Text |
| Slug | Short Text |
| Content Model | Field Name | Field Type |
|---|---|---|
| Post | Title | Short Text |
| Slug | Short Text | |
| Content | Long Text | |
| Tag | Reference |
このときの型定義は次のようになります。タグには自身を参照しているエントリの数を格納するcountも定義しています。
export type Tag = {
name: string
slug: string
count: number
}
export type Post = {
title: string
slug: string
content: string
tag: Entry<Tag>
}
タグの一覧を取得する
まず、タグの一覧を取得します。パラメータの型はanyにしていますが、実際は許容する検索パラメータの型を指定します。
export async function getTags(params?: any): Promise<Entry<Tag>[]> {
const entries = await client.getEntries<Tag>({
content_type: 'tag',
...params,
})
return entries.items
}
タグへの参照数を取得する
次に、取得したそれぞれのタグへの参照数を取得していきます。
export async function withLinksCountToTag(
tags: Entry<Tag>[]
): Promise<Entry<Tag>[]> {
await Promise.all(
tags.map(async (tag) => {
const entries = await client.getEntries({
links_to_entry: tag.sys.id,
})
tag.fields.count = entries.total
return tag
})
)
return tags
}
参照数はJSONオブジェクトのtotalプロパティに格納されているので、これを使います。また、このコード例のようにgetTagsをラップするようにして使うことで、参照数が必要ないときにgetTagsのみを使えるようになります。
上記のコード例は、タグの数が多いとパフォーマンス上の問題やAPIのRate Limitによる制限が起きる可能性があります。本サイトはタグが数個かつNext.jsのStatic Site Generationを用いているので問題ありませんが、アプリケーションの種類によって対策を取る必要があると思います。
使い方
この二つの関数を使うと、次のようにしてタグの一覧と、それぞれのタグへの参照数を取得できます。
const tags = await withTagEntriesCount(await getTags())
まとめ
あるエントリを参照する複数のContent Modelがあるとき、参照されるエントリの参照数を取得するのにlinks_to_entryを使うといいです。特定のContent Modelを対象に参照数を取得するときは、links_to_entryではなくContent Typeを指定する方がよいパフォーマンスを期待できます。