Day18 - Astro Series: Content Collection Category

Astro 系列文第十八日:實作集合分類功能

一個漂亮的漸層背景上面有一句標題:「實作集合分類功能」

前言

前面學習了內容集合與頁籤的製作,今天的主題練習透過整理集合抓取到的資料更進一步製作分類功能頁面。

定義問題

隨著內容集合發布的文章越來越多,裡面可能會有許多相同性質的文章你會想要集合起來並替他們分類,並且自動根據類別自動創建頁面方便查閱。希望達成以下需求:

  • 替每個文章添加類別資料
  • 創建一個展示當前集合中所有類別的頁面
  • 創建個別的類別頁面並在其中顯示所有該類別的文章

替每個文章添加類別資料

在集合的設定檔中定義好 category 資料,如無輸入時則預設給予 unsorted 字串。(該 post 集合記得要回傳)

const post = defineCollection({
schema: z.object({
isDraft: z.boolean().default(true),
category: z.string().default('unsorted'),
}),
});

集合定義好之後就可以開始在 content 當中撰寫 Markdown 或 MDX 資料了。

創建一個展示當前集合中所有類別的頁面

post/categories 中創建 index.astro 檔案,並索取 post 集合可用的文章內的 category 資料陣列,攤平裝入 Set 中(由於可能有多篇文章具備相同的類別,因此要消除重複的類別),最後再根據字母做分類,這樣便得到 categories 也就是當前該集合所有存在且不重複的分類名稱。

const publishedPosts = await getCollection('post', (post) => (import.meta.env.PROD ? !post.data.isDraft : true));
const categories = [...new Set(publishedPosts.map((post) => post.data.category).flat())].sort((a, b) =>
a.localeCompare(b, 'en', { sensitivity: 'base' }),
);

接著只需要簡單的拿這筆資料在模板當中呈現即可。

{
categories.map((category) => (
<ul>
<li class="text-3xl">
<Card>
<a href={`/post/categories/${category}`}>{category}</a>
</Card>
</li>
</ul>
));
}

創建個別的類別頁面並在其中顯示所有該類別的文章

這裡需要使用到先前學到的動態路由與 getStaticPaths 方法。首先先創建一個動態路由檔案: pages/post/categories/[slug].astro,並抓取當前該集合所有存在且不重複的分類名稱:

export async function getStaticPaths() {
/* 1. 抓取當前該集合所有存在且不重複的分類名稱,資料大致會長得像這樣:
[
'A-分類名稱', 'B-分類名稱', 'C-分類名稱',
]
*/
const publishedPosts = await getCollection('post', (post) => (import.meta.env.PROD ? !post.data.isDraft : true));
const categories = [...new Set(publishedPosts.map((post) => post.data.category).flat())];
/* 2. 最後製作 getStaticPaths 期望接收的資料
[
{ params: { slug: 'A-分類名稱' }, props: { publishedPosts: [Array] } },
{ params: { slug: 'B-分類名稱' }, props: { publishedPosts: [Array] } },
{ params: { slug: 'C-分類名稱' }, props: { publishedPosts: [Array] } },
]
*/
return categories.flatMap((category) => [{ params: { slug: category }, props: { publishedPosts } }]);
}

依照以上的操作順利透過 getStaticPaths 根據分類來創造分類頁面,除了創造頁面之外也替每個頁面傳入了 publishedPosts 的 Props,後續再過濾出跟這個 slug 相關的所有文章並依照時間分類。

const { slug } = Astro.params;
const { publishedPosts } = Astro.props;
const avaliableCurrentCategoryPosts = publishedPosts
.filter((post) => post.data.category === slug)
.sort((a, b) => b.data.publishDate.getTime() - a.data.publishDate.getTime());

總結

在這個分類功能實作當中利用了動態路由與內容集合來產生文章分類頁面,可以在 GitHub 上觀賞我自己部落格的分類頁面作法🔗

延伸閱讀