テンプレートの継承と再利用性
テンプレートの再利用と継承の仕組みは、各テンプレートが固有の内容のみを含み、繰り返される要素や構造は再利用されるため、生産性を向上させることができます。ここでは、レイアウト継承、水平再利用、単位継承の3つのコンセプトを紹介します。
Latteのテンプレート継承のコンセプトは、PHPのクラス継承に似ています。親テンプレートを定義し、他の子テンプレート**がそれを継承し、親テンプレートの一部をオーバーライドすることができます。これは、要素が共通の構造を持つ場合に非常に有効です。複雑に聞こえますか?心配しないでください、そんなことはありません。
レイアウトの継承{layout}
レイアウト・テンプレートの継承について、まず例から見ていきましょう。これは親テンプレートで、例えばlayout.latte
と呼ぶことにします。これは HTML のスケルトン・ドキュメントを定義します。
{block}
タグは、子テンプレートが埋めることのできる 3
つのブロックを定義しています。ブロック・タグが行うのは、子テンプレートが同じ名前の独自のブロックを定義することで、テンプレートのこれらの部分をオーバーライドできることをテンプレート・エンジンに伝えるだけです。
子テンプレートは次のようなものになります。
ここで重要なのは、{layout}
タグです。このタグは、このテンプレートが他のテンプレートを「拡張」していることをテンプレートエンジンに伝えます。Latteがこのテンプレートをレンダリングするとき、まず親を探します。この場合、layout.latte
。
このとき、テンプレートエンジンはlayout.latte
にある3つのブロックタグに注目し、それらのブロックを子テンプレートの内容に置き換えます。子テンプレートでは
footer
ブロックが定義されていないため、代わりに親テンプレートのコンテンツが使用されることに注意してください。親テンプレートの{block}
タグ内のコンテンツは、常にフォールバックとして使用されます。
出力は次のようになります。
子テンプレートでは、ブロックはトップレベルか他のブロックの内部にのみ配置できます。
また、周囲の{if}
の条件が true か false
かにかかわらず、ブロックは常に内側に作成されます。しかし、このテンプレートはブロックを定義しています。
ブロックの中の出力を条件付きで表示させたい場合は、次のようにします。
子テンプレート内のブロック外のデータは、レイアウトテンプレートがレンダリングされる前に実行されるため、{var $foo = bar}
のような変数の定義や、継承チェーン全体へのデータの伝搬に使用することができます。
マルチレベル継承
必要なだけ多くのレベルの継承を使用することができます。レイアウト継承の一般的な使用方法として、次のような3レベルの方法があります。
- サイトの主要な外観を保持する
layout.latte
テンプレートを作成します。 - サイトの各セクションに対応する
layout-SECTIONNAME.latte
テンプレートを作成します。例えば、layout-news.latte
,layout-blog.latte
などです。これらのテンプレートはすべてlayout.latte
を拡張し、セクション固有のスタイルやデザインを含んでいます。 - ニュース記事やブログ記事など、ページの種類ごとに個別のテンプレートを作成します。これらのテンプレートは、適切なセクションテンプレートを拡張します。
動的なレイアウトの継承
親テンプレートの名前を変数や任意のPHP式で指定することで、動的な継承を行うことができます。
また、Latte APIを使用して、レイアウトテンプレートを自動的に選択することもできます。
ヒント
ここでは、レイアウトの継承を行う際のTipsを紹介します。
- テンプレート内で
{layout}
を使用する場合、そのテンプレート内の最初のテンプレートタグでなければなりません。 - レイアウトは自動的に検索することができます(プレゼンターのように)。この場合、テンプレートがレイアウトを持つべきではない場合は、
{layout none}
タグでその旨を表示します。 - タグ
{layout}
はエイリアス{extends}
を持ちます。 - 拡張テンプレートのファイル名は、テンプレート・ローダーによって異なります。
- ブロックはいくつでも用意できます。子テンプレートはすべての親ブロックを定義する必要はないので、いくつかのブロックに合理的なデフォルトを記入し、後で必要なものだけを定義することができることを覚えておいてください。
ブロック{block}
匿名も参照 {block}
ブロックは、テンプレートの特定の部分がどのようにレンダリングされるかを変更する方法を提供しますが、その周りのロジックには何ら干渉しません。次の例で、ブロックがどのように機能するか、そしてより重要なのは、どのように機能しないかを説明します。
このテンプレートをレンダリングした場合、ブロックタグがあってもなくても結果はまったく同じになります。ブロックは外部スコープの変数にアクセスすることができます。これは、子テンプレートでオーバーライド可能にするための手段に過ぎません。
さて、子テンプレートをレンダリングするとき、ループはベーステンプレートparent.Latte
で定義されたブロックではなく、子テンプレートchild.Latte
で定義されたブロックを使用するようになります。
しかし,名前付きブロックの中で新しい変数を作成したり,既存の変数の値を置き換えたりすると,その変更はブロックの中だけに表示されます.
ブロックの内容は、フィルターによって変更することができます。次の例では、すべてのHTMLを削除し、タイトルケースを付けています。
このタグはn:attributeと書くこともできる。
ローカルブロック
すべてのブロックは、同じ名前の親ブロックの内容を上書きします。ただし、ローカルブロックは例外です。これは、クラスのプライベート・メソッドのようなものです。ブロック名の一致により、2つ目のテンプレートで上書きされることを心配することなく、テンプレートを作成することができます。
ブロックの印刷{include}
ブロックを特定の場所に印刷するには、{include blockname}
タグを使用します。
また、別のテンプレートからブロックを表示することもできます。
印刷されたブロックは、アクティブなコンテキストの変数にアクセスできません。ただし、ブロックがインクルードされているのと同じファイルに定義されている場合は例外です。ただし、グローバル変数へのアクセスは可能です。
ブロックに変数を渡すには、次のようにします:
ブロック名として、変数または PHP
の任意の式を使用することができます。この場合、変数の前にblock
というキーワードを追加し、コンパイル時にブロックであることがわかるようにします。
ブロックは、それ自身の内部に出力することもできます。これは、たとえば、ツリー構造をレンダリングするときに便利です。
{include menu, ...}
の代わりに{include this, ...}
と書くこともできます。ここでthis
は現在のブロックを意味します。
印刷されたコンテンツは、フィルターによって変更することができます。次の例では、すべてのHTMLを削除し、タイトルケースを付けています。
親ブロック
親テンプレートからブロックの内容を出力する必要がある場合は、{include parent}
ステートメントを使用すると便利です。これは、親ブロックを完全にオーバーライドするのではなく、親ブロックの内容に追加したい場合に便利です。
定義{define}
Latteにはブロックの他に「定義」というものがあります.これは、通常のプログラミング言語における関数に相当するものです。テンプレートの断片を再利用して、同じことを繰り返さないようにするために便利です。
ラテは物事をシンプルにしようとしているので、基本的に定義はブロックと同じであり、ブロックについて言われたことはすべて定義にも当てはまる。定義がブロックと違うのは
- タグで囲まれる
{define}
- 挿入されたときだけレンダリングされる。
{include}
- PHPの関数のようにパラメータを定義できる。
HTMLフォームの描き方に関する定義を集めたヘルパー・テンプレートがあるとしよう。
定義の引数は、デフォルト値が指定されない限り、常にオプションで、デフォルト値はnull
です(ここでは'text'
が$type
のデフォルト値)。パラメータ・タイプも宣言できます:{define input, string $name, ...}
。
定義を含むテンプレートは {import}
.定義自体はブロックと同じ方法でレンダリングされる:
定義はアクティブ・コンテキストの変数にはアクセスできないが、グローバル変数にはアクセスできる。
動的なブロック名
Latte では、ブロック名を任意の PHP
式にすることができるため、非常に柔軟にブロックを定義することができます。この例では、hi-Peter
,hi-John
,hi-Mary
という名前の3つのブロックを定義しています。
例えば、子テンプレートの中で1つのブロックだけを再定義することができます。
ですから、出力は次のようになります。
ブロックの存在を確認する{ifset}
参照 {ifset $var}
{ifset blockname}
テストを使用して、ブロック (または複数のブロック)
が現在のコンテキストに存在するかどうかを確認します。
ブロック名として、変数あるいは PHP
の任意の式を使用することができます。この場合、変数の前にblock
というキーワードを追加して、チェック対象が変数でないことを明確にします。
ブロックの存在は、関数 hasBlock()
:
ヒント
ブロックを使った作業のコツを紹介します。
- 最後のトップレベルブロックには終了タグは必要ありません(ブロックはドキュメントの終わりで終了します)。これによって、1つの主ブロックを持つ子テンプレートの記述が簡単になります。
- 読みやすくするために、
{/block}
タグに{/block footer}
のような名前を付けることができます。ただし、この名前はブロック名と一致させる必要があります。大きなテンプレートでは、このテクニックを使うと、どのブロックタグが閉じられているのかがわかりやすくなります。 - 同じテンプレートに同じ名前の複数のブロックタグを直接定義することはできません。しかし、動的なブロック名を使えば、これを実現することができます。
- n:attributesを使って、次のようなブロックを定義することができます。
<h1 n:block=title>Welcome to my awesome homepage</h1>
- ブロックは、出力にフィルタを適用するためだけに名前なしで使用することもできます。
{block|strip} hello {/block}
水平方向の再利用{import}
水平方向の再利用は、Latteにおける再利用と継承の3番目のメカニズムです。他のテンプレートからブロックを読み込むことができます。PHPでヘルパー関数を含むファイルを作成し、require
を使って読み込むのと似ています。
テンプレートレイアウトの継承はLatteの最も強力な機能の一つですが、単純な継承に限られています。水平方向の再利用は、複数の継承を実現する方法です。
ブロック定義のセットを用意しよう:
{import}
コマンドを使って、blocks.latte
で定義されたすべてのブロックと定義を別のテンプレートにインポートする:
親テンプレートでブロックをインポートすると(つまり、layout.latte
で{import}
を使用すると)、すべての子テンプレートでもブロックが使用できるようになり、非常に便利です。
インポートしようとするテンプレート(例えば、blocks.latte
)は、他のテンプレートを拡張してはいけません(例えば、{layout}
)。しかし、他のテンプレートをインポートすることはできます。
{import}
タグは、{layout}
の後の最初のテンプレート・タグでなければなりません。テンプレート名には、任意の PHP
式を指定することができます。
{import}
ステートメントは、任意のテンプレートで好きなだけ使用することができます。インポートされた2つのテンプレートが同じブロックを定義している場合、先に定義されたものが優先されます。ただし、メイン・テンプレートが最も優先され、インポートされたブロックを上書きすることができます。
上書きされたブロックの内容は、親ブロックと同じようにブロックを挿入することで保持できる:
この例では、{include parent}
はblocks.latte
テンプレートからsidebar
ブロックを正しく呼び出します。
ユニットの継承{embed}
ユニット継承は、レイアウト継承の考え方をコンテンツ・フラグメントのレベルまで拡張したものです。レイアウト継承が "ドキュメントスケルトン "で動作し、子テンプレートによって命を吹き込まれるのに対して、ユニット継承はコンテンツの小さなユニットのスケルトンを作成し、好きな場所で再利用することが可能です。
ユニット継承では、{embed}
タグがキーとなります。このタグは、{include}
と{layout}
の動作を組み合わせたもので、{include}
のように、他のテンプレートやブロックの内容を取り込んだり、変数を渡したりすることができます。また、{layout}
のように、インクルードされたテンプレートの内部で定義されたブロックをオーバーライドすることも可能です。
例えば、折りたたみ可能なアコーディオン要素を使用することにします。テンプレートcollapsible.latte
にある要素のスケルトンを見てみましょう。
{block}
タグは、子テンプレートが埋めることのできる2つのブロックを定義しています。そう、レイアウト継承のテンプレートの親テンプレートの場合と同じです。また、$modifierClass
変数がありますね。
テンプレートで私たちの要素を使用してみましょう。ここで、{embed}
。これは超強力なキットで、要素のテンプレート・コンテンツをインクルードしたり、変数を追加したり、カスタムHTMLのブロックを追加したりと、あらゆることができるようになります。
出力は次のようになります。
embedタグ内のブロックは、他のブロックとは独立した別レイヤーを形成しています。したがって、embedタグの外側にあるブロックと同じ名前を持つことができ、何ら影響を受けません。{embed}
タグ内にincludeタグを使用すると、ここで作成したブロック、埋め込みテンプレートからのブロック(ローカルではないもの)、メインテンプレートからのブロック(ローカルなもの)を挿入することができます。また、他のファイルからブロックをインポートすることもできます。
埋め込みテンプレートは、アクティブなコンテキストの変数にアクセスできませんが、グローバル変数にはアクセスできます。
{embed}
では、テンプレートだけでなく、他のブロックも挿入することができますので、先ほどの例は次のように記述することができます。
{embed}
に式を渡して、それがブロック名なのかファイル名なのかはっきりしない場合は、キーワードblock
またはfile
を追加してください。
使用例
Latteには様々な種類の継承やコードの再利用があります。より明確にするために、主な概念をまとめてみましょう。
{include template}
使用例:header.latte
&footer.latte
をlayout.latte
の中で使用する場合 .
header.latte
footer.latte
layout.latte
{layout}
ユースケース:layout.latte
をhomepage.latte
&about.latte
の内部に拡張する.
layout.latte
homepage.latte
about.latte
{import}
使用例:sidebar.latte
insingle.product.latte
&single.service.latte
.
sidebar.latte
single.product.latte
single.service.latte
{define}
使用例: いくつかの変数を取得し、いくつかのマークアップを出力する関数です。
form.latte
profile.service.latte
{embed}
ユースケース: pagination.latte
をproduct.table.latte
&service.table.latte
に埋め込む。
pagination.latte
product.table.latte
service.table.latte