ドキュメント

ブログ機能

AK²Engine のブログシステム — データ構造・コレクション・フィルター・ページネーションの完全ガイドです。

← 目次に戻る


概要

AK²Engine は Eleventy のコレクション・フィルター・ページネーション機能を活用した静的ブログシステムを提供します。記事はファイルベースで管理され、カテゴリ・タグ・アーカイブなどの分類はビルド時に自動生成されます。


ブログ記事のデータ構造

フロントマター

各ブログ記事の .md ファイルには以下のフロントマターを記述します。

---
title: "AK²Engine v0.1.4 リリースノート"
date: 2026-03-08
postCategory: "お知らせ"
postTags:
  - ak2engine
  - eleventy
---
フィールド 必須 説明
title 文字列 記事タイトル
date 日付 公開日(YYYY-MM-DD)。コレクションのソート・アーカイブに使用
postCategory 文字列 カテゴリ名(日本語)。categories.json でスラッグに変換
postTags 配列 タグスラッグの配列。postTagNames.json で表示名に変換

必要なデータファイル

ブログ機能を使用するには、サイトの _data/ ディレクトリに以下の 2 ファイルが必要です。

_data/categories.json — カテゴリ名とスラッグの対応表:

{
  "お知らせ": "news",
  "技術メモ": "tech",
  "デザイン": "design"
}

_data/postTagNames.json — タグスラッグと表示名の対応表:

{
  "eleventy": "Eleventy",
  "css": "CSS",
  "javascript": "JavaScript",
  "ak2engine": "AK²Engine"
}

ディレクトリ構造

ブログ記事はパスベースのコレクションで管理されます。ブログ専用ディレクトリ内の .md ファイルが自動的にブログ記事として認識されます。

src/
├── blog/
│   ├── first-post.md
│   ├── second-post.md
│   └── ...
├── _data/
│   ├── categories.json
│   └── postTagNames.json

コレクション

.eleventy.jsaddCollection で事前計算されたコレクション群です。テンプレートから collections.コレクション名 でアクセスできます。

基本コレクション

コレクション名 説明 データ構造
blogAll 全ブログ記事(フィルタなし) Eleventy ページオブジェクトの配列
blog 日付付き記事(新しい順) Eleventy ページオブジェクトの配列(date でソート)

分類用コレクション

コレクション名 説明 データ構造
blogCategorySlugs 全カテゴリスラッグ一覧 Set<string> → 配列
blogTagSlugs 全タグスラッグ一覧 Set<string> → 配列
blogArchiveMonths 年月一覧(降順) ["2026/03", "2026/02", ...]

集計済みコレクション

これらはテンプレートでの算術演算を避けるため、.eleventy.js で事前に集計されたデータです(ルール 14 参照)。

blogArchiveData — 年月階層データ:

[
  {
    year: "2026",
    totalCount: 5,
    months: [
      { month: "03", count: 3 },
      { month: "02", count: 2 }
    ]
  }
]

blogCategoryData — カテゴリ集計データ(件数降順):

[
  { name: "お知らせ", slug: "news", count: 3 },
  { name: "技術メモ", slug: "tech", count: 2 }
]

blogTagData — タグ集計データ(件数降順):

[
  { name: "Eleventy", slug: "eleventy", count: 4 },
  { name: "CSS",      slug: "css",      count: 2 }
]

フィルター

.eleventy.jsaddFilter で定義されたフィルター群です。テンプレートから | フィルター名 で使用できます。

日付フィルター

フィルター 入力 出力 使用例
dateFormat Date オブジェクト YYYY.MM.DD {{ post.date | dateFormat }}
dateIso Date オブジェクト YYYY-MM-DD {{ post.date | dateIso }}
dateYearMonth Date オブジェクト YYYY/MM {{ post.date | dateYearMonth }}

カテゴリ・タグフィルター

フィルター 入力 出力 使用例
catSlug カテゴリ名(日本語) スラッグ文字列 {{ "お知らせ" | catSlug }}"news"
catName スラッグ カテゴリ名(日本語) {{ "news" | catName }}"お知らせ"
tagName タグスラッグ タグ表示名 {{ "eleventy" | tagName }}"Eleventy"

ユーティリティフィルター

フィルター 入力 出力 使用例
isArray 任意の値 真偽値 {% if value | isArray %}
head 配列, 件数 先頭 N 件の配列 {{ posts | head(5) }}

カレンダーフィルター

フィルター 入力 出力
buildCalendar "YYYY/MM" 文字列, 記事コレクション カレンダーデータオブジェクト

buildCalendar は月間カレンダーグリッドを構築する特殊フィルターです。

{% set calData = "2026/03" | buildCalendar(collections.blog) %}

返却されるオブジェクト:

{
  year: "2026",
  month: "03",
  title: "2026年3月",
  weeks: [
    // 各週は7要素の配列(日曜〜土曜)
    [null, null, null, null, null, null, {day: 1, link: null}],
    [{day: 2, link: null}, {day: 3, link: "/blog/sample/"}, ...],
    // ...
  ]
}
  • null: 月外の空セル
  • {day, link}: 日付セル。link は当日に記事があればその URL、なければ null

ページネーション

Eleventy の Pagination 機能を使い、カテゴリ別・タグ別・アーカイブ別のリストページを自動生成できます。

カテゴリ別ページ

---
pagination:
  data: collections.blogCategorySlugs
  size: 1
  alias: catSlug
permalink: "/blog/category/{{ catSlug }}/"
---

タグ別ページ

---
pagination:
  data: collections.blogTagSlugs
  size: 1
  alias: tagSlug
permalink: "/blog/tag/{{ tagSlug }}/"
---

アーカイブ別ページ(年月)

---
pagination:
  data: collections.blogArchiveMonths
  size: 1
  alias: yearMonth
permalink: "/blog/archive/{{ yearMonth }}/"
---

.eleventy.js での設定例

ブログ機能に必要なコレクションとフィルターの実装例です。

module.exports = function(eleventyConfig) {
  const categories = require('./_data/categories.json');
  const tagNames   = require('./_data/postTagNames.json');
  // ---------- コレクション ----------
  // 全ブログ記事(日付順)
  eleventyConfig.addCollection("blog", function(collectionApi) {
    return collectionApi.getAll()
      .filter(p => p.inputPath.startsWith('./src/blog/') && p.inputPath.endsWith('.md'))
      .filter(p => p.data.date)
      .sort((a, b) => b.data.date - a.data.date);
  });
  // カテゴリスラッグ一覧
  eleventyConfig.addCollection("blogCategorySlugs", function(collectionApi) {
    const slugs = new Set();
    collectionApi.getAll()
      .filter(p => p.inputPath.startsWith('./src/blog/') && p.data.postCategory)
      .forEach(p => {
        const slug = categories[p.data.postCategory];
        if (slug) slugs.add(slug);
      });
    return [...slugs];
  });
  // カテゴリ集計データ
  eleventyConfig.addCollection("blogCategoryData", function(collectionApi) {
    const map = {};
    collectionApi.getAll()
      .filter(p => p.inputPath.startsWith('./src/blog/') && p.data.postCategory)
      .forEach(p => {
        const name = p.data.postCategory;
        const slug = categories[name];
        if (!slug) return;
        if (!map[slug]) map[slug] = { name, slug, count: 0 };
        map[slug].count++;
      });
    return Object.values(map).sort((a, b) => b.count - a.count);
  });
  // アーカイブ集計データ
  eleventyConfig.addCollection("blogArchiveData", function(collectionApi) {
    const yearMap = {};
    collectionApi.getAll()
      .filter(p => p.inputPath.startsWith('./src/blog/') && p.data.date)
      .forEach(p => {
        const d = new Date(p.data.date);
        const y = String(d.getFullYear());
        const m = String(d.getMonth() + 1).padStart(2, '0');
        if (!yearMap[y]) yearMap[y] = {};
        yearMap[y][m] = (yearMap[y][m] || 0) + 1;
      });
    return Object.keys(yearMap).sort().reverse().map(year => ({
      year,
      totalCount: Object.values(yearMap[year]).reduce((a, b) => a + b, 0),
      months: Object.keys(yearMap[year]).sort().reverse()
        .map(month => ({ month, count: yearMap[year][month] }))
    }));
  });
  // ---------- フィルター ----------
  eleventyConfig.addFilter("dateFormat", d => {
    const dt = new Date(d);
    const y = dt.getFullYear();
    const m = String(dt.getMonth() + 1).padStart(2, '0');
    const day = String(dt.getDate()).padStart(2, '0');
    return `${y}.${m}.${day}`;
  });
  eleventyConfig.addFilter("catSlug", name => categories[name] || name);
  eleventyConfig.addFilter("catName", slug => {
    for (const [name, s] of Object.entries(categories)) {
      if (s === slug) return name;
    }
    return slug;
  });
  eleventyConfig.addFilter("tagName", slug => tagNames[slug] || slug);
};

前のページ: ← ベストプラクティス | ↑ 目次 | 次のページ: ブログウィジェット →