分類法是套用到一個或多個集合的命名分類。EmDash 從用於文章的階層 category 分類法和平坦的 tag 分類法開始。站台也可以定義諸如 genre、topic 或 difficulty 的分類法。
術語屬於某個分類法。像「Guides」這樣的分類可以有子分類,而標籤和其他平坦分類法只有一層。
管理術語
在 EmDash 管理後台中從 Taxonomies 開啟一個分類法。
-
點擊 Add Category、Add Tag,或目前分類法的對應操作。
-
輸入標籤和 slug。對於階層分類法,當術語屬於另一個術語之下時,選擇父層。
-
新增選用的描述,然後建立術語。
-
使用術語列表中的移動控制項,在目前父層群組內設定順序。
編輯者從內容條目中的分類法面板指派術語。分類法定義控制哪些集合顯示每個面板。
刪除術語會移除其在內容上的指派。不會刪除內容條目本身。
向多篇文章新增一個術語
編輯者可以在集合列表中選取文章,然後點擊 Add 加上該分類法的單數名稱(例如 Add tag),或開啟某個分類法的頁面並點擊 Add to posts 貼上公開文章 URL(每行一個,最多 50 個)。當集合適用多個分類法時,列表會顯示 Add term,對話方塊會詢問要使用哪個分類法。選擇現有術語或在對話方塊中建立一個,然後點擊 Review posts。檢查每個相符的標題和語言,然後點擊顯示將更新多少篇文章的按鈕。與本站台已發佈文章不完全相符的連結會被標記,而不是猜測。只有使用所選分類法的集合中的條目才能被比對。
新增術語會立即生效,即使文章還有其他未發佈的草稿編輯;它不會發佈那些編輯。現有術語保持不變,已有該術語的文章會被略過。結果列表會顯示哪些文章已更新、被略過或無法更新;對寫入失敗請使用 Retry failures。URL 比對使用站台設定的公開來源和集合 URL 模式,包括日期和語言路徑。
新增自訂分類法
當現有集合需要獨立的分類時,建立分類法。
-
開啟 Taxonomies 並點擊 New Taxonomy。
-
輸入標籤和穩定名稱。名稱以小寫字母開頭,僅包含小寫字母、數字和底線。
-
若術語需要父子關係,啟用 Hierarchical。
-
選取可以使用該分類法的每個集合,然後點擊 Create Taxonomy。
-
新增初始術語並將它們指派給內容。
範本查詢穩定名稱。變更顯示標籤不需要變更範本。
自訂分類法使用與分類和標籤相同的查詢與篩選輔助函式。以下範例讀取 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.rewrite("/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 和標籤。
術語的父層和位置由所有語言環境共用。未指定父層而建立的翻譯,會採用其術語的父層和位置。在不同的父層下建立翻譯,或透過任一語言環境變更父層,會在所有語言環境中移動該術語。
查詢輔助函式在提供明確的語言環境時使用該語言環境。否則它們使用目前請求的語言環境,再退回到設定的預設值。請求的翻譯不存在時,單一術語的查找會遵循設定的後備鏈。
有關語言環境路由與後備設定,請參閱國際化 (i18n);有關編輯條目,請參閱處理內容。JavaScript API 參考記錄了分類法查詢輔助函式。對於程式化變更,請使用 Bearer 權杖進行驗證,並向每個會變更狀態的請求加入 X-EmDash-Request: 1。請求本文與回應請參閱分類法端點。