分類法

本頁內容

分類法是套用到一個或多個集合的命名分類。EmDash 從用於文章的階層 category 分類法和平坦的 tag 分類法開始。站點也可以定義諸如 genre、topic 或 difficulty 的分類法。

術語屬於某個分類法。像「Guides」這樣的分類可以有子分類,而標籤和其他平坦分類法只有一層。

管理術語

在 EmDash 管理中從 Taxonomies 開啟一個分類法。

  1. 點擊 Add Category、Add Tag,或目前分類法的等效操作。

  2. 輸入標籤和 slug。對於階層分類法,當術語屬於另一個術語之下時,選擇父級。

  3. 新增選用描述,然後建立術語。

  4. 使用術語列表中的移動控制項,在目前父組內設定順序。

編輯者從內容條目中的分類法面板指派術語。分類法定義控制哪些集合顯示每個面板。

刪除術語會移除其在內容上的指派。不會刪除內容條目本身。

向多篇文章新增一個標籤

編輯者可以在集合列表中選擇文章並點擊 Add tag,或開啟 Tags 並點擊 Add to posts 貼上公開文章 URL(每行一個,最多 50)。選擇現有標籤或在對話框中建立,然後點擊 Review posts。檢查每個相符的標題和語言,然後點擊顯示將標記多少篇文章的按鈕。與本站已發佈文章不完全相符的連結會被標記,而不是猜測。

新增標籤會立即生效,即使文章還有其他未發佈的草稿編輯;它不會發佈那些編輯。現有標籤保持不變,已有該標籤的文章會被跳過。結果列表顯示哪些文章被標記、跳過或無法標記;對寫入失敗使用 Retry failures。URL 比對使用站點設定的公開來源和集合 URL 模式,包括日期和語言路徑。

新增自訂分類法

當現有集合需要單獨分類時,建立分類法。

  1. 開啟 Taxonomies 並點擊 New Taxonomy。

  2. 輸入標籤和穩定名稱。名稱以小寫字母開頭,僅包含小寫字母、數字和底線。

  3. 若術語需要父子關係,啟用 Hierarchical。

  4. 選擇可以使用該分類法的每個集合,然後點擊 Create Taxonomy。

  5. 新增初始術語並將它們指派給內容。

範本查詢穩定名稱。變更顯示標籤不需要變更範本。

自訂分類法使用與分類和標籤相同的查詢與篩選輔助函式。以下範例讀取 genre 術語,並依其中一個 slug 篩選書籍:

import { getEmDashCollection, getTaxonomyTerms } from "emdash";

const genres = await getTaxonomyTerms("genre", { includeCounts: false });
const { entries: scienceFictionBooks } = await getEmDashCollection("books", {
  where: { genre: "science-fiction" },
});

刪除分類法

在管理中開啟分類法,然後從頁首的操作選單中選擇 Delete taxonomy 並確認。該操作需要編輯者和管理員持有的 taxonomies:manage 權限。

刪除分類法會刪除其在每種語言中的術語,並從歸入其下的內容中移除這些術語。內容條目本身會保留。

查詢術語列表

使用 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 輔助函式。

以下路由列出一個分類中的已發佈文章:

---
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 使用分類法名稱作為鍵,術語 slug 作為值。查詢排序識別碼使用資料庫欄位名稱,如 published_at;條目資料將對應值公開為 publishedAt。

在 postHref() 中使用集合的實際公開路由。若集合使用自訂 urlPattern,請從該模式建置連結,而不是假設 /posts/{slug}。

顯示條目的術語

getEmDashEntry() 和 getEmDashCollection() 將已指派術語水合到 entry.data.terms。讀取該值,而不是對列表中的每個條目執行一次 getEntryTerms() 查詢。

以下元件渲染已隨文章載入的分類和標籤:

---
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() 批次取得術語。

翻譯分類法與術語

分類法定義和術語每種語言環境有一行。EmDash 記錄哪些行是同一分類法或術語的翻譯。內容指派使用該共用身分,因此在一種語言環境中做出的指派,在存在翻譯術語時會解析到另一種語言環境中的翻譯術語。

分類法定義將其欄位在分類法與各語言環境之間分配:

欄位屬於效果
name分類法建立後固定。每種語言環境的定義使用相同名稱。
hierarchical、collections分類法每種語言環境相同。透過任一語言環境變更任一者,會對所有語言環境生效。
label、labelSingular語言環境每種語言環境的定義有自己的。

為已在另一語言環境中存在的名稱建立的定義會加入該分類法(無論有無 translationOf),並採用其 hierarchical 和 collections。用不同值建立會失敗;請透過更新變更它們。沒有自己定義的語言環境仍會列出分類法的術語,並顯示其回退鏈上第一個有標籤的語言環境的標籤,否則預設語言環境的標籤,否則語言環境代碼最低的語言環境的標籤。

使用分類法頁面上的語言環境切換器,在每個已設定語言環境中管理術語。開啟術語的編輯對話框,並使用其 Translations 面板新增或開啟另一種語言環境。翻譯術語可以使用不同的 slug 和標籤。

術語的父級和位置由每種語言環境共用。在沒有父級的情況下建立的翻譯會採用其術語的父級和位置。在不同父級下建立翻譯,或透過任一語言環境變更父級,會在每種語言環境中移動該術語。

查詢輔助函式在提供時使用明確語言環境。否則使用目前請求語言環境,然後是設定的預設值。當請求的翻譯不存在時,單術語查找遵循設定的回退鏈。

有關語言環境路由與回退設定,請參見 Internationalization;有關編輯條目,請參見 Working with Content。執行時期 API 參考 記錄了分類法查詢輔助函式。對於程式化變更,使用 Bearer 權杖進行身分驗證,並向每個變更狀態的請求新增 X-EmDash-Request: 1。請求主體與回應請參見 分類法端點。