いわゆる、eval的な挙動になります。あまり文章で説明してもピンと来づらいと思いますので、実際の使用例を先にご紹介します。始めに断っておくと、あまり推奨できる方法ではありません。
ここまでは何も問題ありません。では、forループ内でオフィス名を出力している「item.name」の「name」をハードコードしないで出力したい場合、どうすればよいでしょうか?
基本となるコード
今回は仮に「name」というkeyが、「name_key」という変数に値として格納されているとしましょう。雰囲気的には次のような感じですが、当然次のコードは動きません。
{% raw %}
{% set name_key = 'name' %}
{% set offices = [
{
name: '東京オフィス'
},
{
name: '札幌オフィス'
}
] %}
{%- for item in offices -%}
{{ item.name_key }} {# この呼び出し方では当然動かない #}
{%- endfor -%}
これは次のように書くことで、期待通りの出力を得ることが可能です(変数宣言は同じなので省略します)。
{%- for item in offices -%}
{% set call_value = '{{ item.' + name_key + ' }}' %} {# 変数の呼び出しを文字列として組み立てる #}
{{ call_value }} {# 文字列として組み立てた変数の呼び出しを展開する #}
{%- endfor -%}
ここまでの解説ではただ単に非現実的で、使いどころのないコードに思えます。この方法が必要になるのは、次のように「プロパティの数が可変なテーブル的構造のデータを、自動でで出力したい」という場合です。
実用例
次のデータを先ほどの方法を使用して、tableタグで出力してみます。
{%- for item in offices -%}
{% set offices = {
columns: {
name: '支店名',
address: '住所',
tel: '電話番号'
},
contents: [
{
name: '東京オフィス',
address: '東京都千代田区',
tel: '03-XXXX-XXXX'
},
{
name: '札幌オフィス',
address: '北海道札幌市',
tel: '011-XXX-XXXX'
}
]
} %}
<table>
<tr>
{# 1. 見出しの出力 #}
{%- for val in offices.columns -%}
<th>{{ val }}</th>
{%- endfor -%}
</tr>
{# 2. contentsでループを回す #}
{%- for item in offices.contents -%}
<tr>
{# 3. keyを取得するため、1.と同じようにループを回す #}
{%- for key, val in offices.columns.items() -%}
{# 4. 取得したkeyを使用して、contentsのデータを取り出す変数呼び出しを文字列として組み立てる #}
{% set call_value = '{{ item.' + key + ' }}' %}
{# 5. 4を展開する #}
<td>{{ call_value }}</td>
{%- endfor -%}
</tr>
{%- endfor -%}
</table>
出力結果
<table>
<tr>
<th>支店名</th>
<th>住所</th>
<th>電話番号</th>
</tr>
<tr>
<td>東京オフィス</td>
<td>東京都千代田区</td>
<td>03-XXXX-XXXX</td>
</tr>
<tr>
<td>札幌オフィス</td>
<td>北海道札幌市</td>
<td>011-XXX-XXXX</td>
</tr>
</table>
一気に複雑になってしまったので、順を追って解説します。
- 見出しの出力
まずはcolumnsに格納されている各valueを見出しとして出力します。columnsはディクショナリですが、普通にループを回すとvalue値が取得できます。
- contentsでループを回す
次に、contentsの要素ごとにループを回します。これ自体は特に特別なコードではありません。
- keyを取得するため、dictループを回す
contents内の各ディクショナリのvalueにアクセスするため、columnsの各キーを取得する必要があります。for文をこのように記述するとディクショナリのkeyにもアクセスできるようになり、これはHubSpot CMSの公式ドキュメントにも紹介されている方法です。
- 取得したkeyを使用して、contentsのデータを取り出す変数呼び出しを文字列として組み立てる
本記事で紹介するメインのコードです。先ほどの「基本形」セクションのコードと本質的に同じです。
- 4を展開する
こちらも本記事で紹介するメインのコードで、「基本形」セクションのコードと本質的に同じです。
この状態であれば、次のようにコンテンツが増えても、HTMLの出力をしている側のコードは一切修正する必要がありませんので、つまりデータと出力を疎結合に保てている証となります。
{% set offices = {
columns: {
name: '支店名',
address: '住所',
tel: '電話番号',
staff_num: 'スタッフ数' {# 追加 #}
},
contents: [
{
name: '東京オフィス',
address: '東京都千代田区',
tel: '03-XXXX-XXXX',
staff_num: '10名' {# 追加 #}
},
{
name: '札幌オフィス',
address: '北海道札幌市',
tel: '011-XXX-XXXX',
staff_num: '3名' {# 追加 #}
}
]
} %}
{# 出力の方のコードには一切変更なし #}
出力結果
<table>
<tr>
<th>支店名</th>
<th>住所</th>
<th>電話番号</th>
<th>スタッフ数</th>
</tr>
<tr>
<td>東京オフィス</td>
<td>東京都千代田区</td>
<td>03-XXXX-XXXX</td>
<td>10名</td>
</tr>
<tr>
<td>札幌オフィス</td>
<td>北海道札幌市</td>
<td>011-XXX-XXXX</td>
<td>3名</td>
</tr>
</table>
この方法が何より真価を発揮するのは、カスタムモジュール内でHubDBテーブルを選択できるようにしている場合です。これについては基本形のHubDB記事にて、実例を紹介しています。
まとめ
以上、変数の値を他の変数の呼び出しに使う方法をご紹介しました。この方法をとることによってデータと出力の疎結合性を高められるため、コード設計としても出来ることがかなり広がると思います。
しかし公式ドキュメントで紹介されている方法ではなく、ハックの域を出ないものですので、使用の際は万が一のことを考え、影響範囲が広くなりすぎないようにすることをオススメします。