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

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

今回は、エントリの一覧を出力する際のテンプレートの書き方を見ていきます。
主にエントリの絞り込みに関する内容をまとめるため、フィールドの出力については次回改めて解説する予定です。

エントリの出力を試す

はじめに、すべてのエントリを出力してみましょう。

前回作成した blog/index.twig を開き、{% block mainContent %}{% endblock %} 内の任意の箇所に下記コードを入力の上、確認してみてください。

すべてのエントリを出力

{% for entry in craft.entries %}
  <h2><a href="{{ entry.url }}">{{ entry.title }}</a></h2>
{% endfor %}

サンプルは {% for %} タグのループ処理で個々のエントリを entry にセットし、そこから {{ entry.title }} (タイトル)や {{ entry.url }} (パーマリンク)を出力しています。

craft.entries はエントリ取得用のオブジェクトで、追加されたパラメータにあわせてエントリが絞り込まれます。

最新3件のエントリを出力

{% for entry in craft.entries.limit(3) %}
  <h2><a href="{{ entry.url }}">{{ entry.title }}</a></h2>
{% endfor %}

limit パラメータで、出力件数を指定できます。

指定した投稿日に含まれるエントリを出力

{% for entry in craft.entries.after('2016-05-02').before('2016-05-04') %}
  <h2><a href="{{ entry.url }}">{{ entry.title }}</a></h2>
{% endfor %}

afterbefor パラメータを組み合わせることで、指定した期間のエントリのみに絞り込むことができます。

なお、セットできるパラメータについては、公式リファレンスも参照してみてください。

craft.entries | Templating Reference | Craft CMS:
https://craftcms.com/docs/templating/craft.entries

複雑な絞り込みをする前に

{% set %} タグで取得したオブジェクトを一旦変数にセットしておくと、条件の設定と出力のループ処理を分けて記述できるため、コードの可読性が高くなります。

{# 出力対象のエントリを変数にセット #}
{% set filterdEntries = craft.entries %}

{# 出力対象のエントリをループ処理 #}
{% for entry in filterdEntries %}
  <h2><a href="{{ entry.url }}">{{ entry.title }}</a></h2>
{% endfor %}

以降のサンプルは、この {% set %} タグ部分についての解説となります。

エントリの絞り込み

では、よく使いそうな絞り込みを見てみましょう。

セクションで絞り込む

{% set filterdEntries = craft.entries.section('work') %}

セクションによる絞り込みは section パラメータと 設定 > セクションハンドル 列に書かれた文字列の組み合わせることで可能になります。

サンプルの場合、Work セクションのエントリだけを取得できます。

同一エントリをタイトル、ID、スラグのいずれかで絞り込む

Services セクション、かつ、タイトルが「Design」のエントリをセット

{% set filterdEntries = craft.entries.section('services').title('Design') %}

ID = 129 のエントリをセット

{% set filterdEntries = craft.entries.id(129) %}

Services セクション、かつ、スラグが「design」のエントリをセット

{% set filterdEntries = craft.entries.section('services').slug('design') %}

特定のエントリを取得する場合によく使う記述で、3つとも同じエントリを取得できます。
エントリのタイトルなど同一のものが存在する場合は、 section パラメータなどと組み合わせると良いでしょう。

関連付けされたエントリで絞り込む

{# ID = 129 のエントリを取得 #}
{% set relatedEntry = craft.entries.id(129) %}

{# Work セクションの「Services Performed」フィールドに relatedEntry がセットされているエントリを取得 #}
{% set filterdEntries = craft.entries.section('work').relatedTo({
  targetElement: relatedEntry,
  field: 'servicesPerformed'
}) %}

フィールドタイプがエントリ、ファイル管理、カテゴリー、タグ、ユーザのいずれかに該当する場合、セットされた内容を relatedTo パラメータで絞り込むことができます。サンプルは Services Performed フィールドに Design エントリが関連付けされている Work セクションのエントリを取得するための記述です。

{% set %} タグを2つ記述し、targetElement で参照している点がポイントです。

{# ID = 12345 のカテゴリを取得 #}
{% set relatedCategory = craft.categories.id(12345) %}

このように、対象となる要素とフィールドのハンドル名を調整すれば、カテゴリーなどでも同様に絞り込めます。

なお、カテゴリーとタグの両方を指定するなど複合的な指定も可能ですので、公式リファレンスを確認してみてください。

Relations | Documentation | Craft CMS:
https://craftcms.com/docs/relations

ページを分割するには?

ページ分割は、Craft CMS の独自タグを利用します。

{# News セクションのエントリを取得 #}
{% set filterdEntries = craft.entries.section('news') %}

{# filterdEntries を3件ごとにページ分割 #}
{% paginate filterdEntries.limit(3) as pageInfo, pageEntries %}

{# リクエストされたページのエントリを出力 #}
{% for entry in pageEntries %}
  {% if loop.first %}<ul>{% endif %}
  <li><a href="{{ entry.url }}">{{ entry.title }}</a></li>
  {% if loop.last %}</ul>{% endif %}
{% endfor %}

{# ページャ #}
{% if pageInfo.prevUrl %}<a href="{{ pageInfo.prevUrl }}">Previous</a>{% endif %}
{% if pageInfo.nextUrl %}<a href="{{ pageInfo.nextUrl }}">Next</a>{% endif %}

まず、絞り込んだエントリを {% paginate %} タグでページあたりの表示件数を指定して分割します。
エントリの出力は {% paginate %} タグで指定したオブジェクトをループ処理し、任意の場所にページャを記述する流れです。

従来のエントリの出力を少しカスタマイズするだけで対応できるので、判りやすいですね。
なお、公式サイトのリファレンスでは pageInfo にセットされる変数を確認できます。あわせて、ページャのマークアップをページ番号のリンクテキストにするためのサンプルコードが紹介されていますので、ぜひ確認してみてください。

{% paginate %} | Templating Reference | Craft CMS:
https://craftcms.com/docs/templating/paginate

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

当初 ブログ セクションの一覧にする予定でしたが、エントリが登録されてない可能性があるため、News セクションの一覧を出力する形にしてみました。

{#
 # ブログ:インデックス テンプレート
 # -----------------
 #
 # 「ブログ」セクションの一覧ページ用テンプレート。
-#}

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

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

{% includeCss blogCss %}

{# このテンプレートの mainContent の内容をセット #}
{% block mainContent %}
  <div class="jumbotron">
    <div class="container">
      <h1>Hello, world!</h1>
      <p>This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.</p>
      <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more &raquo;</a></p>
    </div>
  </div>
  <div class="container">
    <h1>News セクションのエントリ一覧</h1>
    {# News セクションのエントリを取得 #}
    {% set filterdEntries = craft.entries.section('news') %}

    {# filterdEntries を3件ごとにページ分割 #}
    {% paginate filterdEntries.limit(3) as pageInfo, pageEntries %}

    {# リクエストされたページのエントリを出力 #}
    {% for entry in pageEntries %}
      {% if loop.first %}<ul>{% endif %}
      <li><a href="{{ entry.url }}">{{ entry.title }}</a></li>
      {% if loop.last %}</ul>{% endif %}
    {% endfor %}

    {# ページャ #}
    {% if pageInfo.prevUrl %}<a href="{{ pageInfo.prevUrl }}">Previous</a>{% endif %}
    {% if pageInfo.nextUrl %}<a href="{{ pageInfo.nextUrl }}">Next</a>{% endif %}
  </div>
{% endblock %}

{% for %} タグには loop.firstloop.last の場合に ul タグを出力するよう、分岐を加えてあります。

まとめ

ここでは、一覧ページのテンプレートを想定し、エントリの絞り込み方法について解説してみました。
基本を抑えれば、意外と簡単に柔軟な絞り込みができることを感じてもらえたら、嬉しいですね。

次回は、エントリの詳細用テンプレートを題材に、フィールドの出力について解説してみようと思います。