第5回:詳細ページを作る


この章でやること

  • news/_entry.twig を作成し、詳細ページを表示できるようにする

  • V2_Entry_Body モジュールで記事本文を出力する

  • 記事ヘッダー(タイトル・日付・カテゴリー・タグ)をTwig変数で実装する

  • グローバル変数を使ってパンくずリストと<title>タグを動的にする

この章の終わりには、一覧から記事をクリックすると記事ごとの本文が表示される状態になります。


1. テンプレートを準備する

現在、トップページのお知らせ一覧からリンクをクリックしても「404 Not Found」になります。詳細ページ用のテンプレートファイル _entry.twig が存在しないためです。

既存の静的ページをひな形にしてテンプレートを作ります。

news/20250807.html をコピーして news/_entry.twig を作成してください。また、news ディレクトリ内の _entry.twig , index.html, page2.html を残して他のファイルを削除してください。

/themes/sample/
├── _top.twig
├── news/
│   └── _entry.twig   ← 追加
├── service/
├── contact/
├── css/
└── images/

この状態でトップページのお知らせ一覧からリンクをクリックすると、「404 Not Found」にはならず、20250807.html の内容がすべてのリンク先で表示されます。どの記事をクリックしても同じページが出る状態です。ここからCMS化を進めていきます。


2. V2_Entry_Body モジュールのスニペットを書く

news/_entry.twig のコードを確認すると、<article></article> の内側が、V2_Entry_Body モジュールを配置する場所です。

module() 関数でデータを取得し、<article> の内側に以下のスニペットを書いてください。

{% set entryBody = module('V2_Entry_Body', null, { eid: EID }) %}

{% for entry in entryBody.items %}
  <article>
    <header>
      <h1>{{ entry.title }}</h1>
      <!-- entry header contents -->
    </header>

    <div>
      {% if entry.body %}
        <div>
          {{ entry.body|raw }}
        </div>
      {% endif %}
    </div>

    <div class="acms-box-medium">
      {{ include('admin/entry/action.twig', { entry }) }}
    </div>
  </article>
{% endfor %}

詳細ページでは、URLで渡ってくる eid(エントリーID)に応じて表示する記事を切り替えます。第2引数に null(モジュールIDなし)、第3引数で eid: EID を渡しています。EID はグローバル変数で、現在表示しているページのURLから取得したエントリーIDが入っています。このように第3引数で eid を渡すことで、一覧からどの記事をクリックしても、クリックした記事の本文が表示されます。

トップページから最新のお知らせ記事をクリックすると、既存の静的HTMLの上にCMSが出力した記事本文が表示されることを確認できます。この時点ではほぼCSSが効いていないプレーンな状態ですが、一覧から詳細ページへの遷移が成立しています。https://ablogcms-tutorial.ddev.site/ でいくつかのお知らせ記事をクリックし、それぞれ異なる記事本文が表示されることを確認してください。

module() 関数の引数について

引数

内容

第1引数

V2モジュール名(例:V2_Entry_Summary)

第2引数

モジュールID(管理画面で設定されたIDを指定)

第3引数

URLコンテキスト(従来の ctx に該当。例:{ bid: BID, eid: EID } など)

※ 「モジュールID」と「URLコンテキスト」は、従来モジュール同様に省略可能です。

{% set entrySummary = module('V2_Entry_Summary') %}

3. 記事ヘッダー情報を整える

記事本文の前に表示するタイトル・日付・カテゴリー・タグを整えます。スニペット内の <!-- entry header contents --> の部分を以下のコードに置き換えてください。

変換前(静的HTML)

<p class="text-base font-semibold text-sky-600 text-center">
  <time datetime="2025-08-07">2025年8月7日</time>
</p>
<h1 class="mt-2 text-3xl font-bold ...">新しい季節限定メニューが登場しました</h1>
<div class="mt-6 flex flex-wrap items-center justify-center gap-x-4 gap-y-2">
  <a href="#" class="bg-sky-100 text-sky-800 ...">新商品</a>
  <a href="#" class="text-sm text-gray-500 ...">#季節限定</a>
  <a href="#" class="text-sm text-gray-500 ...">#夏メニュー</a>
</div>

変換後(Twig変数)

<p class="text-base font-semibold text-sky-600 text-center">
  <time datetime="{{ entry.datetime|date('Y-m-d') }}">
    {{ entry.datetime|date('Y年m月d日') }}
  </time>
</p>
<h1 class="mt-2 text-3xl font-bold ...">{{ entry.title }}</h1>
<div class="mt-6 flex flex-wrap items-center justify-center gap-x-4 gap-y-2">
  {% if entry.category.items is not empty %}
    <a href="{{ entry.category.items[0].url }}" class="bg-sky-100 text-sky-800 ...">
      {{ entry.category.items[0].name }}
    </a>
  {% endif %}
  {% for tag in entry.tags %}
    <a href="{{ tag.url }}" class="text-sm text-gray-500 hover:text-gray-700">
      #{{ tag.name }}
    </a>
  {% endfor %}
</div>

置き換えポイント解説

日付フォーマット

{{ entry.datetime|date('Y年m月d日') }}

日付は entry.datetime プロパティを使い、Twig標準の date フィルターでフォーマットします。

カテゴリーリンク

{% if entry.category.items is not empty %}
  <a href="{{ entry.category.items[0].url }}">{{ entry.category.items[0].name }}</a>
{% endif %}

カテゴリーは entry.category.items の配列で取得し、0個目(entry.category.items[0])のカテゴリーを表示します。

タグのループ

{% for tag in entry.tags %}
  <a href="{{ tag.url }}">#{{ tag.name }}</a>
{% endfor %}

4. 本文の表示について

手順2のスニペットにはすでに本文出力が含まれています。本文は entry.body で取得し、{{ entry.body|raw }} で出力します。prose prose-lgTailwind Typography プラグインのクラスで、本文テキストに読みやすいスタイルを一括適用します。

{% if entry.body %}
  <div class="mt-12 prose prose-lg max-w-none prose-sky">
    {{ entry.body|raw }}
  </div>
{% endif %}

ここまで実装できたら、<article></article> 内に残っている静的なコンテンツをすべて削除してください。管理画面で入力した本文の内容だけが表示される状態になります。


5. 詳細ページにも管理ボックスを設置する

詳細ページにも管理ボックスを設置し、記事を見ながら直接投稿・編集操作ができるようにします。

news/_entry.twig{% set entryBody = module('V2_Entry_Body') %} の直前に追加してください。

@include("/admin/action.html")

次に、前章と同様に <head> タグ内に管理UI用リソースを追加します。

    <link rel="stylesheet" href="/css/acms-admin.min.css">
    {% set js = module('V2_Js') %}
    <script src="{{ JS_LIB_JQUERY_DIR }}jquery-{{ JS_LIB_JQUERY_DIR_VERSION }}.min.js" charset="UTF-8"></script>
    <script src="{{ ROOT_DIR }}acms.js{{ js.arguments }}" charset="UTF-8" id="acms-js"></script>

これで管理ボックスが正しいデザインで表示され、記事本文の下にエントリー編集ボックスも現れます。


6. パンくずリストと <title> タグを動的にする

記事本文以外にも、タイトルを使っている箇所があります。このままでは、どの記事を開いても同じタイトルが表示されます。グローバル変数を使って動的に出力するよう修正します。

グローバル変数とは

通常の変数({{ entry.title }} など)はモジュールのデータ取得後にしか使えません。グローバル変数は、現在表示しているページのコンテキストから自動生成され、テンプレート内のどこでも使える変数です。Twig版では {{ 変数名 }} の記法でそのまま出力できます。

パンくずリスト

<li>
  <span class="flex items-center">
    <svg class="h-5 w-5 flex-shrink-0 text-gray-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M5.555 17.776l8-16 .894.448-8 16-.894-.448z" /></svg>
    <span class="ml-2 text-sm font-medium text-gray-500" aria-current="page">{{ ENTRY_TITLE }}</span>
  </span>
</li>

標準テンプレートとの差分:グローバル変数

変数

標準テンプレート形式

Twig形式

記事タイトル

%{ENTRY_TITLE}

{{ ENTRY_TITLE }}

サイトルートパス

%{ROOT_DIR}

{{ ROOT_DIR }}

カテゴリーコード

%{CCD}

{{ CCD }}

Twig版では通常の変数と同じ {{ }} 記法で書けるため、グローバル変数だけ別の記法を覚える必要がありません。


<title> タグ

<title>{{ ENTRY_TITLE }} | SAMPLEPLE</title>

https://ablogcms-tutorial.ddev.site/ でいくつかのお知らせ記事をクリックし、ブラウザのタブに記事ごとの正しいタイトルが表示されることを確認してください。


まとめ

この章でやったこと:

  • news/_entry.twig を作成し、module('V2_Entry_Body', null, { eid: EID }) で本文を出力した

  • {% for entry in entryBody.items %} ループ内で記事ヘッダー(タイトル・日付・カテゴリー・タグ)をTwig変数で実装した

  • {% for tag in entry.tags %} でタグの一覧を出力した

  • {{ ENTRY_TITLE }} などグローバル変数をパンくずリストと <title> タグに適用した

  • モジュールID news_body を作成し、URLからのエントリーID引数を設定した

次の章では最後のメインテンプレートである一覧ページを整えます。サムネイル画像の出力・校正オプション・ページャーのTwig実装を学びましょう。