Craft CMS のテンプレートについて理解しよう(エントリの詳細用テンプレート編)

これは Craft CMS Advent Calendar 2016 18日目の記事です。

今回は、エントリの詳細ページのテンプレートの書き方について見ていきます。
ファイル管理フィールドや行列フィールドに関わる記述は他のフィールドタイプの出力時に応用できますので、ポイントを押さえておきましょう。

はじめに

前回作成した一覧ページからのリンク先になる、エントリ詳細ページ用テンプレートを用意します。
管理画面で 設定 > セクション > News と進み、エントリのテンプレートを blog/_entry に変更して保存しましょう。

News セクションの編集画面

この時点から News セクションのエントリ詳細ページは blog/_entry.twig を参照するようになっていますので、ベースとなる下記コードを記述しておきます。

{#
 # ブログ:詳細 テンプレート
 # -----------------
 #
 # 「ブログ」セクションの詳細ページ用テンプレート。
-#}

{# ベーステンプレートとして _layout_bootstrap を継承 #}
{% extends '_layout_bootstrap' %}

{# このテンプレートのみに追加する、スタイル定義をセット #}
{% set blogCss %}
  body {
    padding-top: 50px;
  }
{% endset %}

{% includeCss blogCss %}

{# エントリのタイトルを変数にセット #}
{% set contentTitle = entry.title %}

{# このテンプレートの mainContent の内容をセット #}
{% block mainContent %}
  <div class="container">
    <div class="row">
      <h1>{{ contentTitle }}</h1>
    </div>
  </div>
{% endblock %}

一覧ページと構成は同様ですが、エントリのタイトルを一旦変数にセットしています。

{% set contentTitle = entry.title %}

最後に _layout_bootstrap.twig を開き、title タグに {% if %} タグを追加します。

<title>{% if contentTitle %}{{ contentTitle }} | {% endif %}{{ siteName }}</title>

これで、テンプレートに変数 pageTitle に値がセットされていれば、エントリの詳細ページに限らず title に追記されるようになりました。

コンテンツの出力

詳細ページ用テンプレートでは、 entry.{フィールドのハンドル} でコンテンツを取得できます。
News セクションの場合、 プレーンテキストリッチテキスト に入力されたコンテンツの出力は下記のようなコードになります。

リッチテキスト(Short Description)

{{ entry.shortDescription }}

リッチテキスト フィールドの入力値はデフォルトで p タグで囲まれるため、そのまま出力してよいでしょう。

プレーンテキスト(見出し)

{% if entry.subheading %}
  <h2>{{ entry.heading }}</h2>
{% endif %}

プレーンテキスト フィールドの出力時に任意のマークアップを追加する場合は、 {% if %} タグでコンテンツの有無を判別します。

HTML タグを含むプレーンテキスト(Subheading)

{% if entry.subheading %}
  <h3>{{ entry.subheading|raw }}</h3>
{% endif %}

プレーンテキスト フィールドの入力値に HTML タグを含む場合は、 raw フィルタが必要です。

メタ情報(著者、投稿日)の出力

ユーザネーム

{{ entry.author.username }}

エントリの著者に関する情報は entry.author.{ハンドル} で取得できます。
Happy Leager の admin ユーザは未入力のため管理画面で設定し直す必要がありますが、氏名などを出力する際は下記を参考にしてください。

  • ファーストネーム entry.author.firstName
  • ラストネーム entry.author.lastName
  • フルネーム entry.author.fullName

フルネームは、ファーストネームとラストネームを半角スペース区切りで結合したテキストとなります。

投稿日

{{ entry.postDate|date('Y年n月j日') }}

投稿日は entry.postDate で取得できます。Twig の date フィルタで任意の書式に変更します。

ファイル管理フィールドの出力

ファイル管理フィールドでは、フィールドの設定画面で指定された リミット を上限に複数のアセットを追加できるため、2つの書き方が可能です。

関連付けを行うエントリ、ファイル管理、カテゴリー、タグ、ユーザの他、複数のコンテンツを保持できるチェックボックス、テーブル、マルチセレクトボックスといったフィールドは、同様の記述を行うことになります。

複数のアセットを出力する場合

{% set featuredImages = entry.featuredImage %}
{% for image in featuredImages %}
<a href="{{ image.url }}" class="thumbnail">
  <img src="{{ image.getUrl('thumb') }}" width="{{ image.getWidth('thumb') }}" height="{{ image.getHeight('thumb') }}" alt="{{ image.title }}" />
</a>
{% endfor %}

この場合、エントリ一覧のテンプレートと同様に {% set %} タグで一旦変数にセットした後、{% for %} タグでループ処理します。

最初に登録されたアセットだけを出力する場合

{% set image = entry.featuredImage.first() %}
{% if image %}
  <figure>
    <img src="{{ image.url }}" width="{{ image.width }}" height="{{ image.height }}" alt="{{ image.title }}" />
  </figure>
{% endif %}

最初の1件を first ファンクションで取得し、アセットがセットされていれば {% if %} タグ内のコードを出力します。

【余談】画像の変形について

画像の変形 一覧

WordPress の メディア設定 のイメージに近いのですが、出力するファイルの種類が画像の場合、設定 > ファイル管理 > 画像の変形 で用意した設定でトリミングやリサイズをすることができます。

{% set image = entry.featuredImage.first() %}
<img src="{{ image.getUrl('thumb') }}" width="{{ image.getWidth('thumb') }}" height="{{ image.getHeight('thumb') }}" />

getUrlgetWidth などのファンクションの引数に作成済みのハンドルをセットすると、整形後の画像 URL や画像サイズを取得します。サンプルコードでは、名称 Thumb の画像の変形にあわせて画像がトリミングされます。

テンプレート単位で 画像の変形 を指定する方法も公式リファレンスに掲載されていますので、あわせて確認しておきましょう。

Image Transforms | Documentation | Craft CMS:
https://craftcms.com/docs/image-transforms

行列フィールドの出力

行列フィールドは、ブロックタイプごとに内包するフィールドの構成が変わるため、ループ処理で「どのブロックを出力するのか?」を判別する必要があります。

下記は News セクションでも利用している 設定 > フィールド > Article Body を例とするサンプルコードです。

{# 行列フィールド:Article Body を変数にセット #}
{% set matrixBlocks = entry.articleBody %}

{# Article Body に保存されたブロックのループ処理 #}
{% for block in matrixBlocks %}
  {# ブロックタイプのハンドルを変数にセット #}
  {% set blockType = block.type.handle %}

  {# ブロックタイプごとに、マークアップを調整 #}
  {% switch blockType %}
    {# New Section ブロックタイプ #}
    {% case 'newSection' %}
      <h1>{{ block.sectionHeading }}</h1>
    {# Heading ブロックタイプ #}
    {% case 'heading' %}
      <h2>{{ block.heading }}</h2>
    {# Text ブロックタイプ #}
    {% case 'text' %}
      {{ block.text }}
    {# Pull Quote ブロックタイプ #}
    {% case 'pullQuote' %}
      <blockquote>
        <p>{{ block.pullQuote }}</p>
      </blockquote>
    {# Image ブロックタイプ #}
    {% case 'image' %}
      {% set image = block.image.first() %}
      {% if image %}
        <figure><img src="{{ image.url }}" alt="{{ image.title }}" /></figure>
      {% endif %}
    {# Gallery ブロックタイプ #}
    {% case 'gallery' %}
      {% for image in block.images %}
        <figure>
          <img src="{{ image.getUrl('small') }}" alt="{{ image.title }}" />
          <figcaption>{{ image.shortDescription }}</figcaption>
        </figure>
      {% endfor %}
    {# Quote ブロックタイプ #}
    {% case 'quote' %}
      <blockquote>
        <p>{{ block.quote }}</p>
      </blockquote>
  {% endswitch %}
{% endfor %}

一見すると複雑そうに感じるかもしれませんが、大きく2つの工程に分けられます。
はじめに Article Body フィールドを変数にセットし、ブロックタイプごとにループ処理します。

{% set matrixBlocks = entry.articleBody %}

{% for block in matrixBlocks %}
  (ループ処理のコード)
{% endfor %}

次に、その時点のブロックタイプのハンドルを変数にセットし、 {% switch %} タグでブロックタイプごとに分岐します。

{% set blockType = block.type.handle %}

{% switch blockType %}
  {% case 'newSection' %}
    (New Section の場合の処理)
  {% case 'heading' %}
    (Heading の場合の処理)
{% endswitch %}

あとは、ブロックタイプごとのマークアップを調整しましょう。

Happy Leager に含まれるテンプレート _includes/article_body.html では、前後のブロックタイプで選択された 位置を選択 フィールドにあわせてマークアップを変更する方法を確認できます。カスタマイズの際に参考になると思いますので、一度目を通してみてください。

blog/_entry.twig の最終的なコード

ここまでで紹介した内容をまとめたサンプルコードを掲載しておきます。

{#
 # ブログ:詳細 テンプレート
 # -----------------
 #
 # 「ブログ」セクションの詳細ページ用テンプレート。
-#}

{# ベーステンプレートとして _layout_bootstrap を継承 #}
{% extends '_layout_bootstrap' %}

{# このテンプレートのみに追加する、スタイル定義をセット #}
{% set blogCss %}
  body {
    padding-top: 50px;
  }

  figure > img {
    max-width: 100%;
    height: auto;
  }
{% endset %}

{% includeCss blogCss %}

{# エントリのタイトルを変数にセット #}
{% set contentTitle = entry.title %}

{# このテンプレートの mainContent の内容をセット #}
{% block mainContent %}
  <div class="container">
    <div class="row">
      {# タイトル #}
      <h1>{{ contentTitle }}</h1>
      {# ユーザネームと投稿日 #}
      <p><i class="glyphicon glyphicon-user"></i> by {{ entry.author.username }} | <i class="glyphicon glyphicon-calendar"></i> {{ entry.postDate|date('Y年n月j日') }} </p>
      <hr>
      <div class="row">
        <div class="col-md-2">
          {# Featured Image:ループ処理 #}
          {% set featuredImages = entry.featuredImage %}
          {% for image in featuredImages %}
            <a href="{{ image.url }}" class="thumbnail">
              <img src="{{ image.getUrl('thumb') }}" width="{{ image.getWidth('thumb') }}" height="{{ image.getHeight('thumb') }}" alt="{{ image.title }}" />
            </a>
          {% endfor %}
        </div>
        <figure class="col-md-10">
          {# Short Description #}
          {{ entry.shortDescription }}

          {# 見出し #}
          {% if entry.subheading %}
            <h2>{{ entry.heading }}</h2>
          {% endif %}

          {# Subheading #}
          {% if entry.subheading %}
            <h3>{{ entry.subheading|raw }}</h3>
          {% endif %}

          {# Featured Image:最初の1件目だけを出力 #}
          {% set image = entry.featuredImage.first() %}
          {% if image %}
            <figure>
              <img src="{{ image.url }}" width="{{ image.width }}" height="{{ image.height }}" alt="{{ image.title }}" />
            </figure>
          {% endif %}

          <section>
            {# 行列フィールド:Article Body を変数にセット #}
            {% set matrixBlocks = entry.articleBody %}

            {# Article Body に保存されたブロックのループ処理 #}
            {% for block in matrixBlocks %}
              {# ブロックタイプのハンドルを変数にセット #}
              {% set blockType = block.type.handle %}

              {# ブロックタイプごとに、マークアップを調整 #}
              {% switch blockType %}
                {# New Section ブロックタイプ #}
                {% case 'newSection' %}
                  <h1>{{ block.sectionHeading }}</h1>
                {# Heading ブロックタイプ #}
                {% case 'heading' %}
                  <h2>{{ block.heading }}</h2>
                {# Text ブロックタイプ #}
                {% case 'text' %}
                  {{ block.text }}
                {# Pull Quote ブロックタイプ #}
                {% case 'pullQuote' %}
                  <blockquote>
                    <p>{{ block.pullQuote }}</p>
                  </blockquote>
                {# Image ブロックタイプ #}
                {% case 'image' %}
                  {% set image = block.image.first() %}
                  {% if image %}
                    <figure><img src="{{ image.url }}" alt="{{ image.title }}" /></figure>
                  {% endif %}
                {# Gallery ブロックタイプ #}
                {% case 'gallery' %}
                  {% for image in block.images %}
                    <figure>
                      <img src="{{ image.getUrl('small') }}" alt="{{ image.title }}" />
                      <figcaption>{{ image.shortDescription }}</figcaption>
                    </figure>
                  {% endfor %}
                {# Quote ブロックタイプ #}
                {% case 'quote' %}
                  <blockquote>
                    <p>{{ block.quote }}</p>
                  </blockquote>
              {% endswitch %}
            {% endfor %}
          </section>
        </div>
      </div>
    </div>
  </div>
{% endblock %}

まとめ

ここでは、詳細ページのテンプレートを例に、エントリに紐づいたフィールドの出力方法について解説してみました。
Craft CMS では、管理画面で入力した ハンドル名 とフィールドタイプさえ把握できれば、ある程度カスタマイズできてしまう点が便利ですね。

さて、Advent Calendar の担当日数も残り少なくなってきたため、テンプレートの話題はこのくらいにして w
次回は、おすすめのプラグインについてまとめてみようと思います。