Craft CMS で質問と答えのサービスを作ってみる - STEP07:プロジェクト個別ページ実装

ステップ・バイ・ステップで Craft CMS を使って質問と答えをやり取りできるサービスを作ってみるシリーズのステップ7です。引き続きテンプレートを実装します。

Craft CMS Logo

こんにちは。この記事は「Craft CMS Advent Calendar 2021」22日目の記事です。

Craft CMS を使って質問と答えをやり取りできるサービスをステップ・バイ・ステップで作ってみるという企画をやっていて、今日はその7日目です。

前回の STEP 6 でトップページのテンプレートを実装しましたので、さらに実装を進めていきます。

エラーが出てる?

その前に、現状のテンプレートだと、ログインセッションが切れると下図のようなエラーがでます。

Clean Shot 2021 12 27 07 37 45

このエラーは「nullid にアクセスできません」ということで、つまり currentUser.idcurrentUsernull なので id が取れませんよ、という意味です。currentUser はログイン中のユーザー情報が入ってくるので、ログインセッションが切れてしまっているのでこれが null になっているわけです。

これについては追って実装しますので、今は一度管理画面にログインして実装を進めましょう。

設計変更

今日の記事を書くに当たって、「プロジェクト」というフィールドを一つ追加しました。もしまだ作成されていない場合は STEP 2 を再確認して「プロジェクト」フィールドを追加しておいてください(今日の記事の中では使いません)。

プロジェクト個別ページの実装

今回はプロジェクトの個別ページを実装します。ここには質問と答えをリストアップしたいと思います。

まず、templates ディレクトリに _pages というディレクトリを作成し、その中に下記のテンプレートを書いた project.twig を作成します。

{#=======================================================
 #
 # Extends, Imports
 #
 =======================================================#}
{% extends '_layout/base-page.twig' %}

{#=======================================================
 #
 # ページ固有変数・処理
 #
 =======================================================#}

{#=======================================================
 #
 # 共通変数
 #
 =======================================================#}

{#=======================================================
 #
 # 出力
 #
 =======================================================#}
{% block contentBody %}
    <h1>{{ entry.title }}</h1>
{% endblock %}

この状態で、先日作成したトップページでプロジェクトをクリックすると下図のようにプロジェクト名が表示されます。

Clean Shot 2021 12 27 08 03 12

もしここで entry が定義されていないといった類いのエラーが出たら、管理画面で「設定 > セクション > プロジェクト」に移動し「サイト設定」のところの「テンプレート」が「_pages/project」になっているか確認してください。

Clean Shot 2021 12 27 08 06 25

質問をプロジェクトに紐付ける

次にプロジェクトに「質問・答え」を紐付けるため、「エントリ > プロジェクト」から各プロジェクトの編集画面に移動します。

Clean Shot 2021 12 27 09 20 33

紐付ける「質問」を選択します。ここでは質問だけを紐付ければOKです。

Clean Shot 2021 12 27 09 23 00

質問が紐付いたのを確認して保存します。

Clean Shot 2021 12 27 09 26 31

これで質問がプロジェクトに紐付きました。

プロジェクトに紐付いた質問を表示

続いて、project.twig を下記のように変更し、プロジェクトに紐付いた質問を表示します。

{#=======================================================
 #
 # Extends, Imports
 #
 =======================================================#}
{% extends '_layout/base-page.twig' %}

{#=======================================================
 #
 # ページ固有変数・処理
 #
 =======================================================#}
{% set comments = entry.comments.all() %}

{#=======================================================
 #
 # 共通変数
 #
 =======================================================#}

{#=======================================================
 #
 # 出力
 #
 =======================================================#}
{% block contentBody %}
    <h1>{{ entry.title }}</h1>
    {% if comments|length %}
        <ul>
            {% for comment in comments %}
                <li><a href="{{ comment.url }}">{{ comment.title }}</a></li>
            {% endfor %}
        </ul>
    {% endif %}
{% endblock %}

具体的には、「ページ固有変数・処理」のところに下記の1行を追加し、プロジェクトの「質問・答え」フィールドで選択した質問を取得しています。

{% set comments = entry.comments.all() %}

そして、「出力」の「block contentBody」のところでループでそれを取り出します。

{% block contentBody %}
    <h1>{{ entry.title }}</h1>
    {% if comments|length %}
        <ul>
            {% for comment in comments %}
                <li><a href="{{ comment.url }}">{{ comment.title }}</a></li>
            {% endfor %}
        </ul>
    {% endif %}
{% endblock %}

これで下図のように質問が表示されました。

Clean Shot 2021 12 27 09 37 50

質問に紐付いている答えを表示

最後に質問に紐付いた答えを表示させます。contentBody の中身を下記のように変更します。

{% block contentBody %}
    <h1>{{ entry.title }}</h1>
    {% if comments|length %}
        <ul>
            {% for comment in comments %}
                {% if comment.commentTitle %}
                    <li>{{ comment.commentTitle }}</li>
                {% endif %}
                <li>{{ comment.body }}</li>
                {% set descendants = craft.entries().descendantOf(comment).all() %}
                {% for descendant in descendants %}
                    <li style="margin-left: {{ descendant.level - 1 }}em;">{{ descendant.body }}</li>
                {% endfor %}
            {% endfor %}
        </ul>
    {% endif %}
{% endblock %}

ポイントは {% set descendants = craft.entries().descendantOf(comment).all() %} のところです。

これは、ループ中の comment に紐付いている子孫(descendant)をすべて取得する、という意味です。

見た目で分かりやすいように style="margin-left: {{ descendant.level - 1 }}em;" を付けて孫の答えたちはインデントしています。

Clean Shot 2021 12 27 09 52 58

今日はここまでにしましょう。

Published 2021-12-22
Updated 2021-12-28