ブログを作成する

このページ

このガイドでは、コンテンツタイプの定義からカテゴリやタグを使った投稿の表示まで、EmDashでブログを作成する方法を説明します。

前提条件

  • EmDashサイトがセットアップされ実行中であること(はじめにを参照)
  • Astroコンポーネントの基本的な知識

Posts Collectionの定義

EmDashはセットアップ時にデフォルトの「posts」collectionを作成します。管理ダッシュボードまたはAPIからカスタマイズできます。

デフォルトのposts collectionには以下が含まれます:

  • title - 投稿タイトル
  • slug - URLフレンドリーな識別子
  • content - リッチテキスト本文
  • excerpt - 短い説明
  • featured_image - ヘッダー画像(オプション)
  • status - 下書き、公開済み、または予約済み
  • publishedAt - 公開日(システムフィールド)

最初の投稿を作成する

  1. /_emdash/admin で管理ダッシュボードを開く

  2. サイドバーの Posts をクリック

    タイトル、ステータス、日付を表示するEmDash投稿一覧
  3. New Post をクリック

    タイトル、コンテンツ、公開オプションを含むEmDash投稿エディタ
  4. タイトルを入力し、リッチテキストエディタを使ってコンテンツを作成

  5. サイドバーパネルでカテゴリとタグを追加

  6. ステータスを 公開済み に設定

  7. 保存 をクリック

投稿はすぐに公開され、即座に表示されます。

サイトに投稿を表示する

すべての投稿を一覧表示

以下のページはすべての公開済み投稿を表示します:

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

const { entries: posts } = await getEmDashCollection("posts", {
  status: "published",
});

// 公開日順にソート、新しいものを先に
const sortedPosts = posts.sort(
  (a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0)
);
---

<Base title="ブログ">
  <h1>ブログ</h1>
  <ul>
    {sortedPosts.map((post) => (
      <li>
        <a href={`/blog/${post.data.slug}`}>
          <h2>{post.data.title}</h2>
          <p>{post.data.excerpt}</p>
          <time datetime={post.data.publishedAt?.toISOString()}>
            {post.data.publishedAt?.toLocaleDateString()}
          </time>
        </a>
      </li>
    ))}
  </ul>
</Base>

単一の投稿を表示

以下の動的ルートは個別の投稿をレンダリングします:

---
import { getEmDashCollection, getEmDashEntry } from "emdash";
import { PortableText, Image } from "emdash/ui";
import Base from "../../layouts/Base.astro";

export async function getStaticPaths() {
  const { entries: posts } = await getEmDashCollection("posts", {
    status: "published",
  });

  return posts.map((post) => ({
    params: { slug: post.data.slug },
  }));
}

const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);

if (!post) {
  return Astro.redirect("/404");
}
---

<Base title={post.data.title}>
  <article>
    {post.data.featured_image && (
      <Image image={post.data.featured_image} alt="" />
    )}
    <h1>{post.data.title}</h1>
    <time datetime={post.data.publishedAt?.toISOString()}>
      {post.data.publishedAt?.toLocaleDateString()}
    </time>
    <PortableText value={post.data.content} />
  </article>
</Base>

カテゴリとタグの追加

EmDashにはカテゴリとタグの組み込みタクソノミーが含まれています。用語の作成と管理の詳細についてはタクソノミーを参照してください。

カテゴリで投稿をフィルタリング

以下のルートは単一カテゴリの投稿を一覧表示します:

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

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

  // 階層的なカテゴリをフラット化
  const flatten = (terms) => terms.flatMap((t) => [t, ...flatten(t.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>}

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

投稿のカテゴリを表示

以下のコンポーネントは投稿に割り当てられたカテゴリとタグを表示します:

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

interface Props {
  postId: string;
}

const { postId } = Astro.props;
const categories = await getEntryTerms("posts", postId, "category");
const tags = await getEntryTerms("posts", postId, "tag");
---

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

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

ページネーションの追加

投稿が多いブログの場合、以下のルートで投稿リストをページネーションします:

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

const POSTS_PER_PAGE = 10;

export async function getStaticPaths() {
  const { entries: allPosts } = await getEmDashCollection("posts", {
    status: "published",
  });

  const totalPages = Math.ceil(allPosts.length / POSTS_PER_PAGE);

  return Array.from({ length: totalPages }, (_, i) => ({
    params: { page: String(i + 1) },
    props: { currentPage: i + 1, totalPages },
  }));
}

const { currentPage, totalPages } = Astro.props;

const { entries: allPosts } = await getEmDashCollection("posts", {
  status: "published",
});

const sortedPosts = allPosts.sort(
  (a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0)
);

const start = (currentPage - 1) * POSTS_PER_PAGE;
const posts = sortedPosts.slice(start, start + POSTS_PER_PAGE);
---

<Base title={`ブログ - ${currentPage}ページ`}>
  <h1>ブログ</h1>

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

  <nav>
    {currentPage > 1 && (
      <a href={`/blog/page/${currentPage - 1}`}>前へ</a>
    )}
    <span>{currentPage} / {totalPages} ページ</span>
    {currentPage < totalPages && (
      <a href={`/blog/page/${currentPage + 1}`}>次へ</a>
    )}
  </nav>
</Base>

RSSフィードの追加

以下のエンドポイントはブログのRSSフィードを生成します:

import rss from "@astrojs/rss";
import { getEmDashCollection } from "emdash";

export async function GET(context) {
	const { entries: posts } = await getEmDashCollection("posts", {
		status: "published",
	});

	return rss({
		title: "私のブログ",
		description: "EmDashで構築されたブログ",
		site: context.site,
		items: posts.map((post) => ({
			title: post.data.title,
			pubDate: post.data.publishedAt,
			description: post.data.excerpt,
			link: `/blog/${post.data.slug}`,
		})),
	});
}

フィードは @astrojs/rss パッケージに依存しています。以下のコマンドでインストールします:

npm install @astrojs/rss

次のステップ