タクソノミー
このページで分かること
- タクソノミーとタームの関係、最初からある
category(階層あり)とtag(階層なし)、管理画面でのタームの管理と独自のタクソノミーの追加 getTaxonomyTerms()によるターム一覧の取得と、タームごとの記事一覧(アーカイブページ)の作り方- エントリーに割り当てたタームの表示と、タクソノミー・タームの翻訳
このページの目次
タクソノミー(Taxonomy)は、1つ以上のコレクションに適用する、名前の付いた分類です。EmDashには最初から、投稿(posts)用に、階層を持つ category タクソノミーと、階層のない tag タクソノミーがあります。サイトは genre、topic、difficulty のようなタクソノミーを独自に定義することもできます。
ターム(Term)はタクソノミーに属します。「Guides」のようなカテゴリーは子カテゴリーを持てますが、タグなどの階層のないタクソノミーは1階層だけです。
タームの管理
EmDashの管理画面の「分類グループ」から、タクソノミーを開きます。
-
「Categoryを追加」、「Tagを追加」、または開いているタクソノミーの同様の操作をクリックします。
-
ラベルとスラッグを入力します。階層のあるタクソノミーで、タームを別のタームの下に置く場合は、親を選びます。
-
必要に応じて説明を追加し、タームを作成します。
-
ターム一覧の移動の操作を使い、同じ親のグループ内での順番を設定します。
編集者は、コンテンツのエントリーにあるタクソノミーのパネルからタームを割り当てます。どのコレクションにどのパネルを表示するかは、タクソノミーの定義で決まります。
タームを削除すると、コンテンツへの割り当ても削除されます。コンテンツのエントリーは削除されません。
やさしい解説
タクソノミーは、WordPressの「カテゴリー」と「タグ」にあたる仕組みです。EmDashにも最初から、親子関係を持てる category と、階層のない tag があります。管理画面では「分類グループ」というメニュー名で表示され、その中の1つ1つの項目(「Guides」など)がタームです。WordPressと同じく、タームを削除しても記事そのものは消えず、記事からそのタームの割り当てが外れるだけです。
独自のタクソノミーの追加
既存のコレクションに別の分類が必要になったら、タクソノミーを作成します。
-
「分類グループ」を開き、「分類グループを追加」をクリックします。
-
ラベルと、変更しない前提の名前を入力します。名前は小文字の英字で始め、小文字の英字、数字、アンダースコアだけを使います。
-
タームに親子関係が必要な場合は、「階層構造を使用する(カテゴリーのように親子関係を設定できます)」を有効にします。
-
そのタクソノミーを使えるコレクションをすべて選択し、「分類グループを作成」をクリックします。
-
最初のタームを追加し、コンテンツに割り当てます。
テンプレートは、この変更しない名前でクエリします。表示用のラベルを変えても、テンプレートを変更する必要はありません。
独自のタクソノミーも、カテゴリーやタグと同じクエリ用・絞り込み用のヘルパーを使います。次の例は、genre のタームを読み込み、そのうち1つのスラッグで書籍(books)を絞り込みます。
import { getEmDashCollection, getTaxonomyTerms } from "emdash";
const genres = await getTaxonomyTerms("genre", { includeCounts: false });
const { entries: scienceFictionBooks } = await getEmDashCollection("books", {
where: { genre: "science-fiction" },
});
やさしい解説
WordPressでは、独自の分類を追加するのにプラグインや register_taxonomy() のコードが必要でした。EmDashでは管理画面の「分類グループ」から作成でき、使うコレクションもそこで選びます。ここで入力する「名前」は、テンプレートのコードから呼び出すときの識別子です。あとから表示用のラベルは変えられますが、名前はテンプレートが参照し続けるため、最初に決めた名前を使い続けます。
ターム一覧の取得
タクソノミーの索引ページ、ナビゲーションの一覧、絞り込みの選択肢を描画するには、getTaxonomyTerms() を使います。階層のあるタクソノミーは、各タームの children 配列を通じてツリーを返します。
タームの件数は初期状態で含まれ、そのタクソノミーを割り当てたコレクション全体での集計が必要になります。コンポーネントが件数を表示しない場合は、この処理を省きます。
次のコンポーネントは、件数なしでカテゴリーのリンクを描画します。
---
import { getTaxonomyTerms } from "emdash";
import { getRelativeLocaleUrl } from "astro:i18n";
const locale = Astro.currentLocale;
const categories = await getTaxonomyTerms("category", {
locale,
includeCounts: false,
});
function categoryHref(slug: string) {
const path = `/category/${slug}`;
return locale ? getRelativeLocaleUrl(locale, path) : path;
}
---
<nav aria-label="Categories">
<ul>
{categories.map((category) => (
<li>
<a href={categoryHref(category.slug)}>{category.label}</a>
{category.children.length > 0 && (
<ul>
{category.children.map((child) => (
<li><a href={categoryHref(child.slug)}>{child.label}</a></li>
))}
</ul>
)}
</li>
))}
</ul>
</nav>
コンポーネントで使用件数を表示する場合は、includeCounts: false を省き、term.count を描画します。この件数には、クエリに使ったロケールで一般に公開されているエントリーが含まれます。
タクソノミーのアーカイブページの作成
タームを検索する前に、動的ルートのパラメーターをデコードします。タームとコンテンツは同じロケールでクエリし、生成するパスはAstroのロケールURLヘルパーに通します。
次のルートは、1つのカテゴリーに属する公開済みの投稿を一覧にします。
---
import { decodeSlug, getEmDashCollection, getTerm } from "emdash";
import { getRelativeLocaleUrl } from "astro:i18n";
import Base from "../../layouts/Base.astro";
const locale = Astro.currentLocale;
const slug = decodeSlug(Astro.params.slug);
const category = slug
? await getTerm("category", slug, { locale, includeCounts: false })
: null;
if (!category) {
return Astro.redirect("/404");
}
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
locale,
where: { category: category.slug },
orderBy: { published_at: "desc" },
});
function postHref(postSlug: string) {
const path = `/posts/${postSlug}`;
return locale ? getRelativeLocaleUrl(locale, path) : path;
}
---
<Base title={`${category.label} posts`}>
<h1>{category.label}</h1>
{category.description && <p>{category.description}</p>}
{posts.length > 0 ? (
<ul>
{posts.map((post) => (
post.data.slug && (
<li>
<a href={postHref(post.data.slug)}>{post.data.title}</a>
</li>
)
))}
</ul>
) : (
<p>No posts in this category.</p>
)}
</Base>
where は、タクソノミー名をキー、タームのスラッグを値にします。クエリの並び順の識別子には published_at のようなデータベースのフィールド名を使います。エントリーのデータでは、対応する値が publishedAt として公開されます。
postHref() には、そのコレクションの実際の公開ルートを使います。コレクションが独自の urlPattern を使っている場合は、/posts/{slug} を前提にせず、そのパターンからリンクを組み立てます。
やさしい解説
WordPressでは、カテゴリーごとの記事一覧(アーカイブページ)をテーマの category.php などが自動で表示していました。EmDashでは、このページの例のように、Astroのページファイル(src/pages/category/[slug].astro)を自分で用意します。ページはURLからスラッグを取り出して getTerm() でタームを探し、見つかれば getEmDashCollection() の where でそのタームの記事だけを取得します。タームが見つからない場合は /404 に移動します。
エントリーのタームの表示
getEmDashEntry() と getEmDashCollection() は、割り当てられたタームを entry.data.terms に読み込みます。一覧のエントリーごとに getEntryTerms() のクエリを1回ずつ実行するのではなく、この値を読みます。
次のコンポーネントは、投稿と一緒に読み込み済みのカテゴリーとタグを描画します。
---
import type { ContentEntry, InferCollectionData } from "emdash";
import { getRelativeLocaleUrl } from "astro:i18n";
interface Props {
post: ContentEntry<InferCollectionData<"posts">>;
}
const { post } = Astro.props;
const locale = Astro.currentLocale;
const categories = post.data.terms?.category ?? [];
const tags = post.data.terms?.tag ?? [];
function termHref(taxonomy: string, slug: string) {
const path = `/${taxonomy}/${slug}`;
return locale ? getRelativeLocaleUrl(locale, path) : path;
}
---
{categories.length > 0 && (
<ul aria-label="Categories">
{categories.map((category) => (
<li>
<a href={termHref("category", category.slug)}>{category.label}</a>
</li>
))}
</ul>
)}
{tags.length > 0 && (
<ul aria-label="Tags">
{tags.map((tag) => (
<li>
<a href={termHref("tag", tag.slug)}>{tag.label}</a>
</li>
))}
</ul>
)}
手元にあるのがコレクション名とエントリーIDだけの場合は、getEntryTerms() を使います。コンテンツのクエリでタームが読み込まれていない複数のエントリーについて、タームをまとめて取得するには、getTermsForEntries() を使います。
タクソノミーとタームの翻訳
タクソノミーの定義とタームは、ロケールごとに1行ずつ持ちます。EmDashは、どの行が同じタクソノミーやタームの翻訳なのかを記録します。コンテンツへの割り当てはこの共通のIDを使うため、あるロケールで行った割り当ては、別のロケールに翻訳済みのタームがあれば、そのタームに解決されます。
各ロケールのタームを管理するには、タクソノミーのページのロケール切り替えを使います。タームの編集ダイアログを開き、「翻訳」パネルから別のロケールを追加するか開きます。翻訳したタームは、別のスラッグとラベルを使えます。
クエリ用のヘルパーは、ロケールが明示的に指定されていればそれを使います。指定がなければ、現在のリクエストのロケールを使い、次に設定された既定のロケールを使います。1つのタームを検索する場合、要求された翻訳がなければ、設定されたフォールバックの順番に従います。
ロケールのルーティングとフォールバックの設定は多言語対応を、エントリーの編集はコンテンツの操作を参照してください。タクソノミーのクエリ用ヘルパーはランタイムAPIリファレンスで説明しています。プログラムから変更する場合は、Bearerトークンで認証し、状態を変更するすべてのリクエストに X-EmDash-Request: 1 を付けます。リクエストの本文とレスポンスはタクソノミーのエンドポイントを参照してください。