PowerCMS X ブログ

2021-10-13

新しくなったテンプレート・エンジン

PowerCMS Xのテンプレート・エンジンである PAML(PHP Alternative Template Language)は PowerCMS X ver.3 で刷新され、OSS版も公開しました。

この記事では、新しくなったテンプレート・エンジンについてご紹介します。

準備

PowerCMS X ver.3.0のテンプレートエンジン PAML3では、タグデリミタのカスタマイズによって、Mustache, Twig, Smartyライクな記述が可能になり、変数のスコープの指定やタグ属性への配列やハッシュの指定が簡単になるなど多数の変更があり、一部 if文の評価結果や値の取得結果が従来と異なる可能性があるため、デフォルトでは従来バージョンが利用されるようになっています。環境変数の指定によって新しいバージョンを選択できるようになります。

新しいテンプレートエンジンを利用するには環境変数「paml_version」に「3」を指定、タグデリミタで他のテンプレートエンジンライクな記述を利用するためには「tag_delimiter」にMustache, Twig, Smartyのいずれかを指定します(※paml_versionは管理画面の環境変数では設定できません)。

        "paml_version" : 3,
        "tag_delimiter" : "Mustache",
  • tag_delimiter指定のある時も、標準の記法(MTタグ)は利用できます。
  • MTタグと他の記法を同一のテンプレート内で混在させることはできません。
  • MTタグがテンプレート内にあるとき、常に MTタグが優先されます。
  • Mustache, Twig, Smartyと完全な互換性はなく、if・elseif・unlessタグのtestモディファイアを除き、式などは利用できません。

リニューアルのコンセプト

今回のバージョンアップにあたり、テンプレートエンジンを刷新した理由、そして、目指したものについて少し説明させていただきます。

  • フロントエンド開発における環境変化
  • 初学者にとってのとっつきやすさ
  • 他のテンプレートエンジンやプログラミング言語の作法からより直感的に記述できるように
  • これまでは変数のスコープがすべてグローバルであること

PowerCMS / PowerCMS X のテンプレート記法、MTタグ(MTML) は確かに歴史があり、経験者もそれなりにいるのですが、2021年現在、CMSやテンプレートエンジンのシェア的にも決して安泰ではないと考えました。では、初学者にとってとっつきやすいかどうかを考えると、それもまたどうなんだろうという思いが拭えません。

他のCMS / フレームワークのテンプレートエンジン

ざっと調べると

  • Drupal : Twig
  • Craft CMS : Twig
  • WordPress : PHP
  • RCMS : Smarty
  • a-blog cms : 独自
  • Movable Type : MTML (PowerCMS / PowerCMS Xと同じ)
  • NOREN : 独自
  • WebRelease2 : 独自

情報が公開されていない商用CMSなどは、正しくない場合があるかと思いますが、このような分布? になります。Twigについては CMSではありませんが、PHPの Laravelなどでも利用されています。

一方で、CMSを離れてみると

  • Twig
  • Mustache
  • Smarty
  • eRuby
  • JSP

など、一部重複していますが、このあたりを利用しているケースが多いのではないかと思います。

また、それとは別に JavaScriptフレームワークを好むフロントエンド開発者も増えています。SSG(静的サイトジェネレーター)としても利用できたりして、それが一定の流行になっています。

  • Next.js
  • Gatsby
  • Hugo (Go)
  • Jekyll (Ruby)

PowerCMS X / PowerCMS では静的ファイルを出力できることがこれまでも支持された一つの大きな要因ですし、PowerCMS X では従来比 20倍のパブリッシュ速度を実現してます。インフラ管理が嫌で、じゃぁ、サーバーレスということであれば、クラウドサービス 別ウィンドウで開きます も提供していますし、AWS の S3や CloudFrontとの連携 別ウィンドウで開きます も容易です(AWS連携を強化したことも、この記事で書いているような背景を意識したものではありますが)。

それらを受けて、では、リニューアルにあたって意識したことは以下のようなこととなります。

  • 他のテンプレートエンジンを利用している開発者にとってより直感的に書けること
  • 初学者に学びやすい記法であること
  • CMS(製品)と切り離しても、それが利用できること(OSS/SSG)

ターゲットとしているのは、JavaScriptや PHPなどを普段書いている開発者、開発初心者となります。

より直感的な記述方法に

変数の設定については、これまでは setvarタグもしくは varタグに nameとvalueを指定する方法でした。

<mt:setvar name="_name" value="_value">

新しいバージョンでは var、let、setが利用でき、よりシンプルに書くことができます。このうち letのみブロック内でのみ有効なローカル変数となります。

<mt:var _name=_value>
<mt:let _name=_value>
<mt:set _name=_value>
Mustach互換であれば
{{var _name=_value}}
{{set _name=_value}}
{{let _name=_value}}

※ 変数名の頭に「_」(アンダースコア)を付けているのは、例えば var name=としてしまうと従来からある <mt:var name="値">(変数のセットではなく、出力)と被ってしまうためです。

また、配列を「.」(ドット)で繋ぐことで直感的に扱えるようになります。以下はMustache互換モードでの記述です。

{{let entries.0.title="記事タイトル0"}}
{{let entries.0.text="記事本文0"}}
{{let entries.1.title="記事タイトル1"}}
{{let entries.1.text="記事本文1"}}

forとforeachを他のテンプレートエンジンと同じ記法で

これまで、foreachは単にloopのエイリアス、forはfromからtoまでのループのためのタグでした。

新しいバージョンでは、配列をループ出力する際に、下記のような書き方が可能になります。

<mt:for entry in entries>
  <mt:var name="entry.title">
</mt:for>
Twig互換であれば
{% for entry in entries %}
  {{ entry.title }}
{% endfor %}

foreach は以下のように書けます。

<mt:foreach entries as entry>
  <mt:var name="entry.title">
</mt:foreach>
Mustach互換であれば
{{#foreach entries as entry}}
  {{entry.title}}
{{/foreach}}

これらの組み合わせにより、以下のように書けるようになります。

{{let entries.0.title="記事タイトル0"}}
{{let entries.0.text="記事本文0"}}
{{let entries.1.title="記事タイトル1"}}
{{let entries.1.text="記事本文1"}}
{{#foreach entries as entry}}
  {{entry.title}}
  {{entry.text}}
{{/foreach}}

CMSとテンプレートエンジン・SSGを切り離す

オールインワンで提供できているわけですから、切り離す必要はないように思います(今もそう思います)。ただ、PowerCMS Xは OSSではないですし、こんな話も出てきているわけで、それであれば、我々として一つの回答をさせていただきたいと思ったということでもあります。

新しいテンプレートエンジン PAML3は SSGとして利用できます。以下は Twig互換モードで PowerCMS Xの新バージョンでサポートされた RESTful APIから取得した JSONをスタティックな HTMLとして出力するテンプレートの例です。

{% extends file=extends/twig.tpl %}
{% let _title='Generating static files...' %}
{% block name="title" %}{{ _title|remove_html }}{% endblock %}
{% block name="body" %}
  {# Fetch JSON from API. #}
  {% fetch url=$end_point from_json=results %}
  {{ results.items set=entries }}
  {% for entry in entries %}
    {# Set the local variable in loop. #}
    {% let _title=$entry.title %}
    {# Build each HTML. #}
    {% include file=includes/twig.tpl set=content %}
    {# Generate the path. #}
    {% set path %}{{ base_path }}{% if entry.categories is defined %}{{ entry.categories.0.Path }}/{% endif %}{{ entry.basename }}.html{% endset %}
    {# Output the file. #}
    {% fileput path=$path contents=$content set=result %}
    {# Display or stdout the results. #}
    {% if name=__first__ %}
    {% if name=sapi ne=cli %}<ul>{% endif %}
    {% endif %}
      {% if name=sapi ne=cli %}<li>{% endif %}
      {% if name=result %}
Generated the entry '{{ _title|escape }}' to file '{{ path }}'.
      {% else %}
Failed generate the entry '{{ _title|escape }}' to file '{{ path }}'.
      {% endif %}
      {% if name=sapi ne=cli %}</li>{% endif %}
    {% if name=__last__ %}
    {% if name=sapi ne=cli %}</ul>{% endif %}
    {% endif %}
  {% endfor %}
  {# Get the variable out of the loop. #}
  <p>{{ _title }} done.</p>
{% endblock %}

テンプレートにおける JSONの活用

実はこれまでのバージョンでもできたのですが、from_jsonモディファイアでJSONを配列に変換して変数にセットでき、to_jsonモディファイアで配列をJSON文字列に変換することができます。

<mt:for from_json="entry">
{
    "title" : "記事のタイトル",
    "text" : "<p>これは記事の本文です。<\/p>",
    "published_on": "20211013100000",
    "tags" : ["プレスリリース", "製品情報"]
}
</mt:for>
<h1><mt:var name="entry.title"></h1>
<p class="date">
  公開日 : <mt:var name="entry.published_on" format_ts="Y-m-d H:i:s">
</p>
<div>
  <mt:var name="entry.text">
</div>
<mt:foreach entry.tags as tag>
  <mt:if name="__first__"><ul></mt:if>
    <li><mt:var name="tag"></li>
  <mt:if name="__last__"></ul></mt:if>
</mt:foreach>

新しいバージョンで追加された RESTful APIを利用、もしくは RESTful APIで得られるデータを取得できるテンプレート・タグ「objecttoresource(objecttojson)」ファンクション・タグが追加されています。

従来の記事テンプレートの例

<!DOCTYPE html>
<html lang="<mt:websitelanguage>">
<head>
  <title><mt:entrytitle escape></title>
</head>
<body>
  <h1><mt:entrytitle></h1>
  <div>
    <mt:entrybody>
  </div>
</body>
</html>

新バージョンで同様のものを出力する別の例

{{objectToResouce let="entry"}}
<!DOCTYPE html>
<html lang="{{websitelanguage}}">
<head>
  <title>{{entry.title|escape}}</title>
</head>
<body>
  <h1>{{entry.title}}</h1>
  <div>
    {{entry.text}}
  </div>
</body>
</html>

objecttoresourceタグは、現在のコンテキストのオブジェクトを RESTful APIが返却するデータと同じ形で配列にして返すファンクションタグです。従来の書き方もそのまま可能ですが、新しい書き方であれば、モデルごとに異なるタグ名を使うことなくシンプルなルールさえ覚えればビューを書くことができるようになります。

ブログ内検索

アーカイブ