タクソノミー

このページ

タクソノミーはコンテンツを整理するための分類システムです。EmDashには組み込みのカテゴリとタグが含まれ、特化した分類ニーズに対応するカスタムタクソノミーをサポートしています。

組み込みタクソノミー

EmDashは2つのデフォルトタクソノミーを提供します:

タクソノミータイプ説明
カテゴリ階層型親子関係を持つネストされた分類
タグフラット階層のないシンプルなラベル

どちらもデフォルトでpostsコレクションで利用可能です。

ターム管理

タームの作成

管理ダッシュボード

  1. タクソノミーページに移動します(例:/_emdash/admin/taxonomies/category

  2. 新規追加フォームにターム名を入力します

  3. オプションで設定:

    • スラッグ - URL識別子(名前から自動生成)
    • - 階層型タクソノミー用
    • 説明 - タームの説明
  4. 追加をクリックします

コンテンツエディター

  1. エディターでコンテンツエントリを開きます

  2. サイドバーのタクソノミーパネルを見つけます

  3. カテゴリの場合、該当するタームのチェックボックスをオンにするか、+ 新規追加をクリックします

  4. タグの場合、カンマ区切りでタグ名を入力します

  5. コンテンツを保存します

API

次のリクエストはcategoryタクソノミーにタームを作成します:

POST /_emdash/api/taxonomies/category/terms
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKEN

{
  "slug": "tutorials",
  "label": "チュートリアル",
  "parentId": "term_abc",
  "description": "ハウツーガイドとチュートリアル"
}

タームの編集

  1. タクソノミーのターム一覧ページに移動します

  2. タームの横にある編集をクリックします

  3. 名前、スラッグ、親、または説明を更新します

  4. 保存をクリックします

タームの削除

  1. タクソノミーのターム一覧ページに移動します

  2. タームの横にある削除をクリックします

  3. 削除を確認します

タクソノミーのクエリ

EmDashはタクソノミータームのクエリとタームによるコンテンツフィルタリングの機能を提供します。

すべてのタームを取得

タクソノミーのすべてのタームを取得します:

import { getTaxonomyTerms } from "emdash";

// すべてのカテゴリを取得(ツリー構造を返す)
const categories = await getTaxonomyTerms("category");

// すべてのタグを取得(フラットリストを返す)
const tags = await getTaxonomyTerms("tag");

階層型タクソノミーでは、タームにchildren配列が含まれます:

interface TaxonomyTerm {
	id: string;
	name: string; // タクソノミー名 ("category")
	slug: string; // タームスラッグ ("news")
	label: string; // 表示ラベル ("News")
	parentId?: string;
	description?: string;
	children: TaxonomyTerm[];
	count?: number; // このタームを持つエントリ数
}

countの計算はタクソノミーのコレクション内のすべてのコンテンツ-ターム割り当てを集計するため、呼び出しの最もコストの高い部分です。ラベルとスラッグだけが必要な場合はスキップできます — その場合countは返されるタームから省略されます:

const tags = await getTaxonomyTerms("tag", { includeCounts: false });

単一タームの取得

次の例はタクソノミーとスラッグでタームを取得します:

import { getTerm } from "emdash";

const category = await getTerm("category", "news");
// TaxonomyTermまたはnullを返す

エントリのタームを取得

次の例は単一エントリに割り当てられたカテゴリとタグを取得します:

import { getEntryTerms } from "emdash";

// 記事のすべてのカテゴリを取得
const categories = await getEntryTerms("posts", "post-123", "category");

// 記事のすべてのタグを取得
const tags = await getEntryTerms("posts", "post-123", "tag");

タームでコンテンツをフィルタリング

getEmDashCollectionwhereフィルターとともに使用します:

import { getEmDashCollection } from "emdash";

// 「news」カテゴリの記事
const { entries: newsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: "news" },
});

// 「javascript」タグの記事
const { entries: jsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { tag: "javascript" },
});

または便利な関数を使用します:

import { getEntriesByTerm } from "emdash";

const newsPosts = await getEntriesByTerm("posts", "category", "news");

タクソノミーページの構築

カテゴリアーカイブ

カテゴリの記事を一覧表示するページを作成します:

---
import { getTaxonomyTerms, getTerm, getEmDashCollection } from "emdash";
import Base from "../../layouts/Base.astro";

export async function getStaticPaths() {
  const categories = await getTaxonomyTerms("category");

  function flatten(terms) {
    return terms.flatMap((term) => [term, ...flatten(term.children)]);
  }

  return flatten(categories).map((cat) => ({
    params: { slug: cat.slug },
    props: { category: cat },
  }));
}

const { category } = Astro.props;

const { entries: posts } = await getEmDashCollection("posts", {
  status: "published",
  where: { category: category.slug },
});
---

<Base title={category.label}>
  <h1>{category.label}</h1>
  {category.description && <p>{category.description}</p>}
  <p>{category.count} 記事</p>

  <ul>
    {posts.map((post) => (
      <li>
        <a href={`/blog/${post.data.slug}`}>{post.data.title}</a>
      </li>
    ))}
  </ul>
</Base>

タグアーカイブ

タグの記事を一覧表示するページを作成します:

---
import { getTaxonomyTerms, getEmDashCollection } from "emdash";
import Base from "../../layouts/Base.astro";

export async function getStaticPaths() {
  const tags = await getTaxonomyTerms("tag");

  return tags.map((tag) => ({
    params: { slug: tag.slug },
    props: { tag },
  }));
}

const { tag } = Astro.props;

const { entries: posts } = await getEmDashCollection("posts", {
  status: "published",
  where: { tag: tag.slug },
});
---

<Base title={`「${tag.label}」タグの記事`}>
  <h1>#{tag.label}</h1>

  <ul>
    {posts.map((post) => (
      <li>
        <a href={`/blog/${post.data.slug}`}>{post.data.title}</a>
      </li>
    ))}
  </ul>
</Base>

カテゴリリストウィジェット

記事数付きのカテゴリリストを表示します:

---
import { getTaxonomyTerms } from "emdash";

const categories = await getTaxonomyTerms("category");
---

<nav class="category-list">
  <h3>カテゴリ</h3>
  <ul>
    {categories.map((cat) => (
      <li>
        <a href={`/category/${cat.slug}`}>
          {cat.label} ({cat.count})
        </a>
        {cat.children.length > 0 && (
          <ul>
            {cat.children.map((child) => (
              <li>
                <a href={`/category/${child.slug}`}>
                  {child.label} ({child.count})
                </a>
              </li>
            ))}
          </ul>
        )}
      </li>
    ))}
  </ul>
</nav>

タグクラウド

使用頻度に基づいたサイズでタグを表示します:

---
import { getTaxonomyTerms } from "emdash";

const tags = await getTaxonomyTerms("tag");

const counts = tags.map((t) => t.count ?? 0);
const maxCount = Math.max(...counts, 1);
const minSize = 0.8;
const maxSize = 2;

function getSize(count: number) {
  const ratio = count / maxCount;
  return minSize + ratio * (maxSize - minSize);
}
---

<div class="tag-cloud">
  {tags.map((tag) => (
    <a
      href={`/tag/${tag.slug}`}
      style={`font-size: ${getSize(tag.count ?? 0)}rem`}
    >
      {tag.label}
    </a>
  ))}
</div>

コンテンツにタームを表示

記事にカテゴリとタグを表示します:

---
import { getEntryTerms } from "emdash";

interface Props {
  collection: string;
  entryId: string;
}

const { collection, entryId } = Astro.props;

const categories = await getEntryTerms(collection, entryId, "category");
const tags = await getEntryTerms(collection, entryId, "tag");
---

<div class="post-terms">
  {categories.length > 0 && (
    <div class="categories">
      <span>カテゴリ:</span>
      {categories.map((cat, i) => (
        <>
          {i > 0 && ", "}
          <a href={`/category/${cat.slug}`}>{cat.label}</a>
        </>
      ))}
    </div>
  )}

  {tags.length > 0 && (
    <div class="tags">
      {tags.map((tag) => (
        <a href={`/tag/${tag.slug}`} class="tag">
          #{tag.label}
        </a>
      ))}
    </div>
  )}
</div>

カスタムタクソノミー

特化したニーズのために、カテゴリやタグを超えたタクソノミーを作成します。

カスタムタクソノミーの作成

管理APIを使用してタクソノミーを作成します:

POST /_emdash/api/taxonomies
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKEN

{
  "name": "genre",
  "label": "ジャンル",
  "labelSingular": "ジャンル",
  "hierarchical": true,
  "collections": ["books", "movies"]
}

カスタムタクソノミーの使用

組み込みのタクソノミーと同じ方法でカスタムタクソノミーをクエリして表示します:

import { getTaxonomyTerms, getEmDashCollection } from "emdash";

// すべてのジャンルを取得
const genres = await getTaxonomyTerms("genre");

// ジャンルの本を取得
const { entries: sciFiBooks } = await getEmDashCollection("books", {
	where: { genre: "science-fiction" },
});

コレクションへの割り当て

タクソノミーは適用されるコレクションを指定します:

{
  "name": "difficulty",
  "label": "難易度",
  "hierarchical": false,
  "collections": ["recipes", "tutorials"]
}

タクソノミーAPIリファレンス

RESTエンドポイント

エンドポイントメソッド説明
/_emdash/api/taxonomiesGETタクソノミー定義を一覧
/_emdash/api/taxonomiesPOSTタクソノミーを作成
/_emdash/api/taxonomies/:name/termsGETタームを一覧
/_emdash/api/taxonomies/:name/termsPOSTタームを作成
/_emdash/api/taxonomies/:name/terms/:slugGETタームを取得
/_emdash/api/taxonomies/:name/terms/:slugPUTタームを更新
/_emdash/api/taxonomies/:name/terms/:slugDELETEタームを削除

コンテンツにタームを割り当て

次のリクエストは記事にカテゴリタームを割り当てます:

POST /_emdash/api/content/posts/post-123/terms/category
Content-Type: application/json
Authorization: Bearer YOUR_API_TOKEN

{
  "termIds": ["term_news", "term_featured"]
}

次のステップ