Web雑記帳 atsu666


a-blog cms テンプレート解体新書

December 06 Sun 2015 | a-blog cms

これは、 a-blog cms Advent Calendar 2015 の6日目の記事です。

大袈裟なタイトルがついていますが、a-blog cmsでのテンプレートを組み立てる、ちょっとしたコツなどを紹介したいとおもいます。

テンプレート要素

a-blog cmsのテンプレート要素を大きく2つに分けると

  • モジュールと、そのモジュール内で使用する要素
  • テンプレートのどこでも使用可能な要素

の2つに分類できます。以下にまとめました。


モジュールと、そのモジュール内で使用テンプレート内のどこでも使用可能
GET モジュールinclude
Touch モジュールグローバル変数
変数 校正オプションIFブロック
veilブロック-
touchブロック-

テンプレート要素はそんなにありませんが紛らわしいもの(例えばtouchブロックとTouchモジュールは違うもの)もあるので、整理をしておきましょう。

テンプレートの実行順序

テンプレートの実行順序は非常に重要です。ここが分かってないと、思いどおりにテンプレートが動かなかったり、パフォーマンスを意識した組み立てが出来ないからです。

以下が実行順なります。

  1. グローバル変数
  2. インクルード
  3. Touchブロック
  4. GETモジュールが内側から実行(変数, 校正オプション, veil, touchブロックが解決)
  5. IFブロック

テンプレートの実行順序を利用したテクニックを1つ紹介します。

グローバル変数を利用したインクルード

インクルードの前にグローバル変数が解決される事を利用していかのような事ができます。


<!--#include file="/include/category_%{CCD}.html"--> <!-- %{CCD} は カテゴリーコード -->

例えば news カテゴリがあった場合に、 "/themes/theme/include/categogory_news.html " ファイルがあると、そのファイルをインクルードします。ファイルがなかった場合や違うカテゴリーを表示している場合は、インクルードしません。

これにより、そのカテゴリにいない時に余分なテンプレートが実行されないようになるので、パフォーマンスを意識したテンプレートになります。

逆に以下の例は、どんな時にもインクルードファイルが実行されてしまうので、いいテンプレートとは言えません。


<!-- %{CCD} が何にかかわらずインクルードファイルが読み込まれ実行されてしまいます。-->
<!-- BEGIN_IF [%{CCD}/eq/news] -->
<!--#include file="/include/category_news.html"--> 
<!-- END_IF -->

IFブロックは最後に実行されるので、インクルードが実行されてモジュールが動いた後に、表示を隠します。 これだと必要ない処理が動いている事になります。

大きなテンプレートの塊を表示・非表示する場合は、このようなグローバル変数とインクルードを組み合わせるか、GETモジュールより先に動くTouchモジュールで囲みましょう。

カスタムフィールドの編集を権限によって変更する

カスタムフィールドで項目事に編集権限を分けてみたいと思います。

例えば、会員登録ができるサイトでユーザに自分の情報を入力するカスタムフィールドがあったとします。 それに加えて、管理者がメモとして利用できるカスタムフィールドを用意したいです。(請求情報や要注意人物など。。本人には見せれませんよね。)

まずは、表示・非表示を制御すればいいのですから、Touchモジュールが利用できそうです。


<tr>
  <th>名前</th>
  <td>
    <input type="text" name="name" value="{name}">
    <input type="hidden" name="field[]" value="name">
  </td>
</tr><!-- BEGIN_MODULE Touch_SessionWithAdministration -->
<tr>
  <th>管理用メモ</th>
  <td>
    <textarea name="admin_memo">{admin_memo}</textarea>
    <input type="hidden" name="field[]" value="admin_memo">
  </td>
</tr><!-- END_MODULE Touch_SessionWithAdministration -->

これで、管理者でログインしている時にしか管理用メモは閲覧・編集できなくなりました。ただここで一点問題がでてきます。

a-blog cmsのカスタムフィールドの保存ではそこにある情報(ここではユーザーの情報)をすべて削除してから保存します。なので、ユーザーが自分のプロフィールを変更した時に管理用メモは消えてしまいます。 これでは使い物にならないので、次のように編集します。


<tr>
  <th>名前</th>
  <td>
    <input type="text" name="name" value="{name}">
    <input type="hidden" name="field[]" value="name">
  </td>
</tr><!-- BEGIN_MODULE Touch_SessionWithAdministration -->
<tr>
  <th>管理用メモ</th>
  <td>
    <textarea name="admin_memo">{admin_memo}</textarea>
    <input type="hidden" name="field[]" value="admin_memo">
  </td>
</tr><!-- END_MODULE Touch_SessionWithAdministration -->
</table>
<!-- 以下2行を追加 -->
<input type="hidden" name="updateField" value="on">
<input type="hidden" name="field[]" value="updateField">

最後の2行を追加する事により、管理用メモがない編集画面で保存を行ってもメモが消えないようになります。 一点注意点として、頻繁にテンプレートを変えるとカスタムフィールドのゴミが溜まっていきますのでご注意ください。

検索されたくない情報

カスタムフィールドには、表示させるものだけではなく管理用のためだけのフィールドを用意する事は多いと思います。

ここではスタッフ一覧を表示するページを考えてみたいと思います。スタッフは一人ずつエントリーで作られており、カスタムフィールドには基本的な情報の他に管理情報として、住所などが入力されていたとします。

また、スタッフ一覧ページにはキーワード検索がついていたとします。もちろん表示側には住所の情報はでていないので問題はないかと思うのですが、キーワード検索にはカスタムフィールドの情報も入ってくるので注意が必要です。

キーワード検索に適当な住所をいれて検索すると、その住所のスタッフで絞り込まれてしまうからです。完全な住所はわかりませんが、非公開の情報がある程度予測できてしまう事になります。

対策方法としてカスタムフィールドに以下のような記述をします。


<input type="text" name="address" value="{address}" />
<input type="hidden" name="field[]" value="address" />
<!-- 下を追加 -->
<input type="hidden" name="address:search" value="0">

これにより、住所の情報がフルテキストに入らないようになるので安心です。

veilブロック, touchブロックの動き

veilブロックは、変数が空だった場合に表示を隠すブロックなのですが、挙動がややこしい所があるので、ここでtouchブロックと合わせて紹介しておきます。

例えば、名前とプロフィールの2つのテキスト情報のカスタムフィールドがあり、名前がなかったら両方隠したいと思います。(名前はなくてプロフィールは入力されているものとします) 以下のテンプレートは思った通りの動きをするでしょうか?


<!-- BEGIN name:veil -->
<h3>{name}</h3>
<p>{profile}</p>
<!-- END name:veil -->

みたところ、動きそうな気がします。ただこのコードは動きません。プロフィールが空でないからです。

veilブロックの実際の動き

veilブロックの動きとしてはveilブロックで囲まれた中の変数すべてが空の場合に囲まれたところを非表示にするように動きます。

また、カスタムフィールド名:veil という名前がつけられていますが、実際はブロック名はなんでも構いません。変数を適当なブロックで囲んである状態だとveilブロックとして動きます。

以下のコードは動きます。


<!-- BEGIN hoge -->
<h3>{name}</h3>
<!-- END hoge -->

touchブロックの実際の動き

veilブロックの他にtouchブロックがあります。これはveilと違って値が等しい時に表示をするブロックになります。


<!-- BEGIN color:touch#red -->赤色<!-- END color:touch#red -->

color が red だった場合のみ 赤色 と表示されます。

では以下のコードは動くでしょうか?


<!-- BEGIN color:touch#red -->赤色({color})<!-- END color:touch#red -->

答えは動きません。touchブロックの中に変数{color}があるので、veilブロックとして動いてしまいます。 colorがredなら正しく動きますが、違う色がきたときも表示されてしまいます。

touchブロックはveilと逆でブロックの中には変数を使わないようにしましょう。

ではどうするのか?

では最初の問題のある値が空ならすべて隠すにはどうしたらいいでしょうか?

答えはIFブロックを使用します。下のコードを見てみてください。


<!-- BEGIN_IF [{name}/nem/] -->
<h3>{name}</h3>
<p>{profile}</p>
<!-- END_IF -->

これは意図した通りに動きます。{name}が空だったら問答無用でIFブロックで囲まれた範囲を隠します。

このようにveil, touchブロックはIFブロックで代替可能です。veil, touchブロックを無理に使うよりはIFブロックを使う事をオススメします。パフォーマンス的にも問題ありません。

変数、ブロックのエスケープ

上級テクニックになるのですが、変数やブロックはエスケープする事ができます。これによりどんな事ができるのか見ていきましょう。

例えば、商品をエントリーで管理しているとします。商品にはカスタムフィールドのセレクトボックスで、以下のラベルが設定できるようになっています。

  • 新製品
  • 完売
  • 在庫限り

コードにすると以下になります。


<select name="label">
  <option value="">ラベルを選択</option>
  <option value="新製品"{label:selected#新製品}>新製品</option>
  <option vlaue="完売"{label:selected#完売}>完売</option>
  <option value="在庫限り"{label:selected#在庫限り}>在庫限り</option>
</select>
<input type="hidden" name="field[]" value="label">

これで運用を行なっており、途中で、取寄品・限定品・季節商品などを追加したくなった場合はどうしましょうか?このテンプレートでは、ラベルの種類がハードコーディングされているのでテンプレートを触るしかありません。不便ですよね?

ではここのラベルの選択肢をエスケープを使って動的にしたいと思います。 下準備として、ブログのカスタムフィールドぐらいにカスタムフィールドグループでラベルを設定できるようにしておきます。ここでは省きます。カスタムフィールドメーカーを使えばすぐできると思います。

ではブログのカスタムフィールドを使って先ほどの選択肢を動的にしてみましょう。


<td><!-- BEGIN_MODULE Blog_Field -->
  <select name="label">
    <option value="">ラベルを選択</option><!-- BEGIN item_label:loop -->
    <option value="{item_label}"\{label:selected#{item_label}\}>{item_label}</option><!-- END item_label:loop -->
  </select>
  <input type="hidden" name="field[]" value="label" />
  <!-- END_MODULE Blog_Field -->
</td>

これで、動的にする事ができました。コードを見ていきましょう。まずテンプレートの実行順序を思いだしてください。モジュールは内側から実行されるので、まずBlog_Fieldが動きます。

そこで、{item_label}が解決されてラベルが設定されていきます。では{label:selecte#...}はどうでしょうか?

この変数をよくみてくると \{...\} のように中かっこの前にバックスラッシュが挿入されています。 この状態を変数をエスケープしているといいます。エスケープされた変数はバックスラッシュの数だけモジュールで変数として動きません。またモジュールが実行される度にバックスラッシュが消えていきます。

なので、Blog_Fieldが動いた後のテンプレートが以下になります。


<select name="label">
  <option value="">ラベルを選択</option>
  <option value="新製品"{label:selected#新製品}>新製品</option>
  <option vlaue="完売"{label:selected#完売}>完売</option>
  <option value="在庫限り"{label:selected#在庫限り}>在庫限り</option>
  <option value="取寄品"{label:selected#取寄品}>取寄品</option>
  <option value="限定品"{label:selected#限定品}>限定品</option>
  <option value="季節商品"{label:selected#季節商品}>季節商品</option>
</select>

みての通りハードコーディングしたコードと同じになりました。このようにエスケープを利用するとテンプレートを動的にする事ができるようになります。

動的フォームもこの機能を利用して動作しています。一度覗いてみてはどうでしょうか。

/themes/system/include/form/input.html


変数のエスケープ
\{hoge\}

ブロックのエスケープ
<!-- BEGIN\ hoge --> ... <!-- END\ hoge -->

setTemplate, setRendered ?

年末リリース予定の次期バージョン 2.6.0 では新しいテンプレート要素のsetTemplate, setRendered が追加されます。これはテンプレートを変数化するもので、よりテンプレートが組みやすくなります。 詳しくはリリースされる時まで取っておきたいと思います。

という事でa-blog cmsのテンプレート解体新書でした!


tag : ablogcms adventcalendar