PR
PR

【Astro入門】ブログとその他の記事を分ける!複数のコンテンツコレクションを作成・管理する方法

記事内に広告が含まれています。

前回からの続きです。

【Astro入門】トップページに新着記事一覧を表示!getCollectionを使ったデータ取得と並び替え

今回は、Astroのコンテンツコレクションをさらに活用して、「日記用のブログ」と「専門的な技術記事」のディレクトリを分けて管理する方法を説明します。

例えば、日々の日記は「blog」ディレクトリへ、ゲームの情報や攻略などの内容は「game」ディレクトリへ分けることで、読者にとっても検索エンジン(SEO)にとっても分かりやすいサイト構造を作ることができます。

ディレクトリを分けて管理するメリット

ディレクトリ(コレクション)を分ける最大のメリットは、記事の種類ごとに「ルール(フロントマターの必須項目)」を変えられることです。

  • 日記(blog): タイトルと日付だけで気軽に書けるルール。
  • ゲーム記事(game): タイトル、日付に加えて「タグ(tags)」を必須にするルール

このように、用途に合わせて柔軟なデータ管理が可能になります。

作業内容

今回は、新しく「game」というカテゴリを追加するために、以下の作業を行っていきます。

src/content/gameディレクトリの作成
新しいコレクション(ゲーム記事)のMarkdownファイルを保存するための専用フォルダを作ります。
src/content/config.tsにルール追加
作成した「game」ディレクトリ内の記事に対して、「タグ(tags)を必須にする」といった独自のルール(スキーマ)を追加設定します。
src/pages/gameディレクトリの作成
ブラウザでアクセスしたときのURL(例:http://localhost:4321/game/)の土台となるディレクトリを作ります。
src/pages/game/[slug].astroの作成
「game」ディレクトリに保存された記事データを読み込み、実際にHTMLとして画面に表示するための「個別記事用のテンプレート」を作成します。

前回までの「blog」ディレクトリ作成の作業と近いですね。

新しいディレクトリを作成する

まずは、これまでマークダウン形式の記事を作成していた「src/content/blog」とは別に、新しい「src/content/game」ディレクトリを作成します。

src/content/
├── blog/                      ← 今までの日記用
│    └── 2026/03/13/index.md
└── game/                      ← 新しく作るゲーム記事用ディレクトリ
      └── tails-series/
            └── index.md

「src/content/config.ts」に新しいルールを追記する

Astroでは、プロジェクト全体で「src/content/config.ts」は1つだけと決まっていて、すべてのコレクションのルールをまとめる役割を果たしています。

そのため、新しいディレクトリを作ったからといって、新しい「config.ts」を作成してはいけません。

ですので、既存の「src/content/config.ts」に、以下のように「game」用のルールを書き足します。

import { defineCollection, z } from 'astro:content';

// 今までの blog 用のルール(そのまま残します)
const blogCollection = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    pubDate: z.date(),
    description: z.string(),
    author: z.string(),
  }),
});

// game用のルールを追記
// (tagsを必須にしてみます)
const gameCollection = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    pubDate: z.date(),
    description: z.string(),
    author: z.string(),
    tags: z.array(z.string()), // 記事にはタグを必須にする
  }),
});

// 使用するコレクションにgameを追加
export const collections = {
  'blog': blogCollection,
  'game': gameCollection // ここに追加
};

コードの解説

前回も解説しているので簡単に説明します。

const gameCollection = defineCollection({ ... })
「blogCollection」と同じように、新しく「gameCollection」という名前でルールの塊(スキーマ)を定義します。

  • tags: z.array(z.string()),tagsを必ず追加するルールを追加し、複数のタグを取り込めるように配列(z.array)を使用
export const collections = { ... }
ファイルの一番下にあるこの部分が、Astro本体への「登録リスト」です。
すでに登録されている「'blog': blogCollection」の下に、カンマ(「,」)で区切って「'game': gameCollection」を追加します。

src/pages/gameディレクトリを作成する

Astroは「src/pages」ディレクトリの中身がそのままURLの構造になるというルールがあります。

そのため、「game」というURLを作るために、新しく「src/pages」ディレクトリの中に「game」というディレクトリを作ります。

src/pages/
├── index.astro           ← トップページ
├── blog/
│    └── [slug].astro    ← 今までの日記用ページ
└── game/                 ← 新しく作るディレクトリ
      └── [slug].astro    ← 新しく作るファイル

[slug].astroファイルの作成

記事のデータを取得して表示するための「src/pages/game/[slug].astro」ファイルを新しく作成します。

基本的には「blog」の時とほぼ同じですが、記事の取得先が「game」に変わっているのと、tagsの中身を表示する設定が追加されます。

---
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro'; 

// gameコレクションの記事だけを全て取得して、URLのパスを生成する
export async function getStaticPaths() {
  const gameEntries = await getCollection('game'); // ここが 'game' になります!
  
  return gameEntries.map(entry => {
    // URLを短く綺麗にする処理(blogの時と同じです)
    const cleanSlug = entry.slug.split('/').pop();
    
    return {
      params: { slug: cleanSlug }, 
      props: { entry }, 
    };
  });
}

const { entry } = Astro.props;
const { Content } = await entry.render();
---

<BaseLayout pageTitle={entry.data.title} pageDescription={entry.data.description}>
  <article>
    <h1>{entry.data.title}</h1>
    <p>公開日: {entry.data.pubDate.toLocaleDateString('ja-JP')}</p>
    <p>著者: {entry.data.author}</p>
    <p>
      Tags: {
        entry.data.tags && entry.data.tags.map((tag) => (
          <strong>
            #{tag}
          </strong>
        ))
      }
    </p>
    <hr />
    <Content />
  </article>
</BaseLayout>

コードの解説(blog用との違い)

getCollection('game')
前回は「getCollection('blog')」でしたが、今回はゲーム記事を取得したいので「'game'」 を指定します。
これで Astro が自動的に「src/content/game」の中身を探しに行ってくれます。
entry.data.tags && entry.data.tags.map((tag) => ( ... ))
記事(index.md)のフロントマターで設定したtagsの内容を、配列(例:["RPG", "テイルズ"])から、データを1つずつ「tag」という変数に取り出し、カッコの中のHTML(<strong>タグ)を繰り返し作成します。
最初に「entry.data.tags &&」と書くことで、「もしタグが設定されていなかったらエラーを出さずにスキップする」という安全対策(エラー回避)を行っています。
これは、config.tsでタグを必須にしてますが、後で任意に変更したい場合に備えての設定となります。
#{tag}
取り出したタグの文字の前に「#」をつけて出力しています。これにより「#RPG」のように、よりタグらしい見た目になります。

設定反映

Podman環境を使用している場合、「compose.yaml」ファイルがあるディレクトリで以下のコマンドを実行し、コンテナを再起動して設定内容を反映させます。

$ cd ~/project/astro-dev
$ podman-compose down
$ podman-compose up -d

記事の作成と動作確認

「src/content/game/tails-series/index.md」といった記事を作成しました。

こちらの記事には「tags」を追加しています。

---
title: "テイルズシリーズについて"
pubDate: 2026-03-18
description: "テイルズシリーズについていろいろ書きます"
author: "tamohiko"
tags: ["RPG","テイルズシリーズ"]
slug: "tails-series"  
---

# テイルズシリーズ

ここから下は、普通のMarkdownで本文を書いていきます。

# 発売されているタイトル
- テイルズオブファンタジア

動作確認

その後、ブラウザで「http://localhost:4321/game/tails-series」にアクセスします。

設定したtagsの内容やMarkdownの本文が綺麗に表示されていれば、コレクションの追加と分離は完璧に成功です。

これで、「blog」と「専門的なゲーム記事」を、それぞれ別のURLと別のルールで完全に切り分けて運用できるようになりました。

コメント

タイトルとURLをコピーしました