Sections

Volt: テンプレートエンジン

Volt は、PHPのためにCで記述されており、とても速く、デザイナにも扱いやすいテンプレート言語です。 簡単にビューを書けるように、ヘルパーセットを提供します。 Volt はPhalconの他のコンポーネントと高度に統合されていて、アプリケーションの中で独立したコンポーネントとしても利用できます。

Volt は、Armin Ronacherによって作られたJinjaにインスパイアされています。 そのため、よく似た既存のテンプレートエンジンと同じ記法を採用しており、利用する多くの開発者にとって親しみやすくなっています。 Voltの記法と機能は、Phalconを使う開発者が慣れ親しんだ多くの要素を備え、当然パフォーマンスの点においても強化されています。

はじめに

Voltによるビューは純粋なPHPコードにコンパイルされるので、基本的には手でPHPコードを書く労力を節約することができます:


{# app/views/products/show.volt #}

{% block last_products %}

{% for product in products %}

    * Name: {{ product.name|e }}
    {% if product.status === 'Active' %}
       Price: {{ product.price + product.taxes/100 }}
    {% endif  %}
{% endfor  %}

{% endblock %}

Voltを使うために

他のテンプレートエンジンと同じように、新しい拡張子や標準的な拡張子.phtmlを用いて、Voltをビューコンポーネントに登録することもできます:

<?php

use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Volt;

// Voltをサービスとして登録
$di->set(
    'voltService',
    function ($view, $di) {
        $volt = new Volt($view, $di);

        $volt->setOptions(
            [
                'compiledPath'      => '../app/compiled-templates/',
                'compiledExtension' => '.compiled',
            ]
        );

        return $volt;
    }
);

// Voltをテンプレートエンジンとして登録
$di->set(
    'view',
    function () {
        $view = new View();

        $view->setViewsDir('../app/views/');

        $view->registerEngines(
            [
                '.volt' => 'voltService',
            ]
        );

        return $view;
    }
);

標準的な.phtmlという拡張子を用いる:

<?php

$view->registerEngines(
    [
        '.phtml' => 'voltService',
    ]
);

DIでVoltサービスを指定する必要はありません。 デフォルトの設定でVoltエンジンを利用することができます:

<?php

$view->registerEngines(
    [
        '.volt' => Phalcon\Mvc\View\Engine\Volt::class,
    ]
);

サービスとしてVoltを再利用したくない場合は、無名関数を渡すことでサービス名の代わりにエンジンを登録することができます。

<?php

use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Volt;

// 無名関数によってVoltをテンプレートエンジンとして登録
$di->set(
    'view',
    function () {
        $view = new View();

        $view->setViewsDir('../app/views/');

        $view->registerEngines(
            [
                '.volt' => function ($view, $di) {
                    $volt = new Volt($view, $di);

                    // Set some options here

                    return $volt;
                }
            ]
        );

        return $view;
    }
);

Voltで使用できるオプション:

オプション 説明 デフォルト
autoescape HTMLの自動エスケープをグローバルに利用可能にする false
compileAlways リクエスト毎にテンプレートをコンパイルしなければならない場合、または変更が必要な場合にのみ、Voltに伝えてください false
compiledExtension コンパイル済みのPHPファイルに追加する拡張子 .php
compiledPath コンパイルされたPHPテンプレートが保存される書き込み可能なパス ./
compiledSeparator Voltはコンパイルされたディレクトリに単一のファイルを作成するために、この区切り文字/を¥に置き換えます %%
prefix コンパイルパスのテンプレートにプレフィックスを付加することができます null
stat テンプレートファイルとコンパイルされたパスの違いが存在するかどうかをPhalconがチェックするかどうか true

コンパイルパスは、上記のオプションに従って生成されます。開発者がコンパイルパスを自由に定義したい場合は、無名関数を使用して生成することができます。この関数は、viewsディレクトリのテンプレートへの相対パスを受け取ります。 次の例は、コンパイルパスを動的に変更する方法を示しています:

<?php

// .php拡張子をテンプレートパスに追加するだけ
// コンパイルされたテンプレートは同じディレクトリに残す
$volt->setOptions(
    [
        'compiledPath' => function ($templatePath) {
            return $templatePath . '.php';
        }
    ]
);

// 他のディレクトリに同じ構造で作成
$volt->setOptions(
    [
        'compiledPath' => function ($templatePath) {
            $dirName = dirname($templatePath);

            if (!is_dir('cache/' . $dirName)) {
                mkdir('cache/' . $dirName , 0777 , true);
            }

            return 'cache/' . $dirName . '/'. $templatePath . '.php';
        }
    ]
);

基本的な使い方

ビューは、VoltやPHP、HTMLのコードで構成されます。 Voltモードでは特別なデリミタが使用できます。 {% ... %} is used to execute statements such as for-loops or assign values and {{ ... }}, prints the result of an expression to the template.

下記は、いくつかの基本を示す最小限のテンプレートです:


{# app/views/posts/show.phtml #}
<!DOCTYPE html>
<html>
    <head>
        <title>{{ title }} - An example blog</title>
    </head>
    <body>

        {% if show_navigation %}
            <ul id='navigation'>
                {% for item in menu %}
                    <li>
                        <a href='{{ item.href }}'>
                            {{ item.caption }}
                        </a>
                    </li>
                {% endfor %}
            </ul>
        {% endif %}

        <h1>{{ post.title }}</h1>

        <div class='content'>
            {{ post.content }}
        </div>

    </body>
</html>

Using Phalcon\Mvc\View you can pass variables from the controller to the views. 上記の例では次の変数がビューに渡されています。show_navigationmenutitlepost:

<?php

use Phalcon\Mvc\Controller;

class PostsController extends Controller
{
    public function showAction()
    {
        $post = Post::findFirst();
        $menu = Menu::findFirst();

        $this->view->show_navigation = true;
        $this->view->menu            = $menu;
        $this->view->title           = $post->title;
        $this->view->post            = $post;

        // Or...

        $this->view->setVar('show_navigation', true);
        $this->view->setVar('menu',            $menu);
        $this->view->setVar('title',           $post->title);
        $this->view->setVar('post',            $post);
    }
}

変数

Object variables may have attributes which can be accessed using the syntax: foo.bar. If you are passing arrays, you have to use the square bracket syntax: foo['bar']


{{ post.title }} {# for $post->title #}
{{ post['title'] }} {# for $post['title'] #}

フィルター

Variables can be formatted or modified using filters. The pipe operator | is used to apply filters to variables:


{{ post.title|e }}
{{ post.content|striptags }}
{{ name|capitalize|trim }}

以下は、Voltで利用可能な、ビルトインのフィルタのリストです:

フィルター 説明
abs abs PHP関数を値に適用します。
capitalize ucwords PHP関数を値に適用して文字列を大文字にします
convert_encoding 文字列をある文字セットから別の文字セットに変換します
default 評価された式が空(設定されていないかfalse)である場合のデフォルト値を設定します
e Phalcon\Escaper->escapeHtml() を値に適用します
escape Phalcon\Escaper->escapeHtml() を値に適用します
escape_attr Phalcon\Escaper->escapeHtmlAttr() を値に適用します
escape_css Phalcon\Escaper->escapeCss() を値に適用します
escape_js Phalcon\Escaper->escapeJs() を値に適用します
format sprintf を使って文字列をフォーマットします。
json_encode 値を JSON に変換します
json_decode 値を JSON からPHP形式に変換します
join 区切り文字で配列を結合します join
keys array_keys を使って配列のキーを返します
left_trim PHPの ltrim 関数を値に適用します。 余分なスペースを削除します
length 文字列の長さ、または配列、オブジェクトに含まれるアイテムの数を数えます
lower 文字列を小文字に変更します。
nl2br 改行コード \n をHTMLの改行(<br />)に変更します。 PHP関数 <2>nl2br</2> を使用します
right_trim PHPの rtrim 関数を値に適用します。 余分なスペースを削除します
sort PHP 関数 asort を使用して配列をソートします。
stripslashes PHPの stripslashes 関数を値に適用します。 エスケープされた引用符を削除します
striptags PHPの striptags 関数を値に適用します。 HTMLタグを削除します
trim PHPの trim 関数を値に適用します。 余分なスペースを削除します
upper 文字列を大文字に変更します。
url_encode urlencode PHP 関数を値に適用します。

例:


{# e or escape filter #}
{{ '<h1>Hello<h1>'|e }}
{{ '<h1>Hello<h1>'|escape }}

{# trim filter #}
{{ '   hello   '|trim }}

{# striptags filter #}
{{ '<h1>Hello<h1>'|striptags }}

{# slashes filter #}
{{ ''this is a string''|slashes }}

{# stripslashes filter #}
{{ '\'this is a string\''|stripslashes }}

{# capitalize filter #}
{{ 'hello'|capitalize }}

{# lower filter #}
{{ 'HELLO'|lower }}

{# upper filter #}
{{ 'hello'|upper }}

{# length filter #}
{{ 'robots'|length }}
{{ [1, 2, 3]|length }}

{# nl2br filter #}
{{ 'some\ntext'|nl2br }}

{# sort filter #}
{% set sorted = [3, 1, 2]|sort %}

{# keys filter #}
{% set keys = ['first': 1, 'second': 2, 'third': 3]|keys %}

{# join filter #}
{% set joined = 'a'..'z'|join(',') %}

{# format filter #}
{{ 'My real name is %s'|format(name) }}

{# json_encode filter #}
{% set encoded = robots|json_encode %}

{# json_decode filter #}
{% set decoded = '{'one':1,'two':2,'three':3}'|json_decode %}

{# url_encode filter #}
{{ post.permanent_link|url_encode }}

{# convert_encoding filter #}
{{ 'désolé'|convert_encoding('utf8', 'latin1') }}

コメント

Comments may also be added to a template using the {# ... #} delimiters. All text inside them is just ignored in the final output:


{# note: this is a comment
    {% set price = 100; %}
#}

制御構文の一覧

Volt には、テンプレートで使用するための基本的かつ強力な制御構文が用意されています。

for文

Loop over each item in a sequence. The following example shows how to traverse a set of ‘robots’ and print his/her name:


<h1>Robots</h1>
<ul>
    {% for robot in robots %}
        <li>
            {{ robot.name|e }}
        </li>
    {% endfor %}
</ul>

forループは入れ子にすることもできます:


<h1>Robots</h1>
{% for robot in robots %}
    {% for part in robot.parts %}
        Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br />
    {% endfor %}
{% endfor %}

以下のシンタックスを用いることで、PHPにおける要素のkeysを得ることができます:


{% set numbers = ['one': 1, 'two': 2, 'three': 3] %}

{% for name, value in numbers %}
    Name: {{ name }} Value: {{ value }}
{% endfor %}

必要に応じてifの評価を設定することができます:


{% set numbers = ['one': 1, 'two': 2, 'three': 3] %}

{% for value in numbers if value < 2 %}
    Value: {{ value }}
{% endfor %}

{% for name, value in numbers if name !== 'two' %}
    Name: {{ name }} Value: {{ value }}
{% endfor %}

もし、forの中でelseを定義した場合は、イテレータの結果が 0回のときに、そこに記述した文が実行されるでしょう:


<h1>Robots</h1>
{% for robot in robots %}
    Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br />
{% else %}
    There are no robots to show
{% endfor %}

代替構文:


<h1>Robots</h1>
{% for robot in robots %}
    Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br />
{% elsefor %}
    There are no robots to show
{% endfor %}

ループ制御

breakcontinue文は、ループから抜けたり、現在のブロック内で強制的に次のイテレーションへ移ったりすることができます:


{# skip the even robots #}
{% for index, robot in robots %}
    {% if index is even %}
        {% continue %}
    {% endif %}
    ...
{% endfor %}


{# exit the foreach on the first even robot #}
{% for index, robot in robots %}
    {% if index is even %}
        {% break %}
    {% endif %}
    ...
{% endfor %}

If文

PHPと同じように、if文は、条件式が true または false に評価されるかをチェックします:


<h1>Cyborg Robots</h1>
<ul>
    {% for robot in robots %}
        {% if robot.type === 'cyborg' %}
            <li>{{ robot.name|e }}</li>
        {% endif %}
    {% endfor %}
</ul>

else 文もサポートされています:


<h1>Robots</h1>
<ul>
    {% for robot in robots %}
        {% if robot.type === 'cyborg' %}
            <li>{{ robot.name|e }}</li>
        {% else %}
            <li>{{ robot.name|e }} (not a cyborg)</li>
        {% endif %}
    {% endfor %}
</ul>

switchブロックをエミュレートするifと一緒に、elseif制御フロー構造を使用することができます:


{% if robot.type === 'cyborg' %}
    Robot is a cyborg
{% elseif robot.type === 'virtual' %}
    Robot is virtual
{% elseif robot.type === 'mechanical' %}
    Robot is mechanical
{% endif %}

switch文

if ステートメントの代わりに switch を使用すると、アプリケーションで論理実行パスを作成できます。


{% switch foo %}
    {% case 0 %}
    {% case 1 %}
    {% case 2 %}
        "foo" is less than 3 but not negative
        {% break %}
    {% case 3 %}
        "foo" is 3
        {% break %}
    {% default %}
        "foo" is {{ foo }}
{% endswitch %}


switch ステートメントはステートメントごとに実行されるため、break ステートメントが必要な場合があります。 switch 文と最初の case の間の出力(空白を含む)は、構文エラーになります。 したがって、空白行と空白を消去して、エラーの数を減らすことができます。ここを見てください

switch 無し case


{% case EXPRESSION %}

Fatal error: Uncaught Phalcon\Mvc\View\Exception: Unexpected CASE がthrowされます。

endswitch 無し switch


{% switch EXPRESSION %}

Will throw `Fatal error: Uncaught Phalcon\Mvc\View\Exception: Syntax error, unexpected EOF in ..., there is a 'switch' block without 'endswitch'`.

switch 無し default


{% default %}

Will not throw an error because default is a reserved word for filters like {{ EXPRESSION | default(VALUE) }} but in this case the expression will only output an empty char ‘’ .

ネストした switch


{% switch EXPRESSION %}
  {% switch EXPRESSION %}
  {% endswitch %}
{% endswitch %}

Will throw Fatal error: Uncaught Phalcon\Mvc\View\Exception: A nested switch detected. There is no nested switch-case statements support in ... on line ...

式の無い switch


{% switch %}
  {% case EXPRESSION %}
      {% break %}
{% endswitch %}

Will throw Fatal error: Uncaught Phalcon\Mvc\View\Exception: Syntax error, unexpected token %} in ... on line ...

ループ変数

for ループで使用できる特別な変数の情報を提供します。

変数 説明
loop.index ループの現在の反復回数。 (1始まり)
loop.index0 ループの現在の反復回数。 (0始まり)
loop.revindex ループの終わりからの反復回数(1始まり)
loop.revindex0 ループの終わりからの反復回数(0始まり)
loop.first ループの最初ならtrue。
loop.last ループの最後ならtrue。
loop.length ループするアイテムの数

例:


{% for robot in robots %}
    {% if loop.first %}
        <table>
            <tr>
                <th>#</th>
                <th>Id</th>
                <th>Name</th>
            </tr>
    {% endif %}
            <tr>
                <td>{{ loop.index }}</td>
                <td>{{ robot.id }}</td>
                <td>{{ robot.name }}</td>
            </tr>
    {% if loop.last %}
        </table>
    {% endif %}
{% endfor %}

変数の割り当て

Variables may be changed in a template using the instruction set:


{% set fruits = ['Apple', 'Banana', 'Orange'] %}

{% set name = robot.name %}

ひとつの命令で複数の代入が可能です。


{% set fruits = ['Apple', 'Banana', 'Orange'], name = robot.name, active = true %}

加えて複合代入命令も使用できます:


{% set price += 100.00 %}

{% set age *= 5 %}

次の演算子が使用できます。

演算子 説明
= 代入
+= 値を足す
-= 値を引く
\*= 値を掛ける
/= 値で割る

条件式

Voltは、リテラルや一般的な演算子を含む基本的な式のサポートを提供します。 A expression can be evaluated and printed using the {{ and }} delimiters:


{{ (1 + 1) * 2 }}

表示を行わずに式を評価する必要がある場合、 do ステートメントを使用します。


{% do (1 + 1) * 2 %}

定数

以下のリテラルがサポートされています。

フィルター 説明
'this is a string' 二重引用符または一重引用符間のテキストは文字列として扱われます
100.25 小数点以下の桁数はdouble / floatとして扱われます。
100 小数部のない数値は整数として扱われます
false 定数 ‘false’ はbool値のfalseです
true 定数 ‘true’ はbool値のtrueです
null 定数 ‘null’ はNULLの値です

配列

あなたが PHP 5.3 または 5.4以上を使用している場合、角括弧 [] でリストの値を囲んで配列を作成できます。


{# Simple array #}
{{ ['Apple', 'Banana', 'Orange'] }}

{# Other simple array #}
{{ ['Apple', 1, 2.5, false, null] }}

{# Multi-Dimensional array #}
{{ [[1, 2], [3, 4], [5, 6]] }}

{# Hash-style array #}
{{ ['first': 1, 'second': 4/2, 'third': '3'] }}

中括弧 {} もまた配列やハッシュを定義するために使用します。


{% set myArray = {'Apple', 'Banana', 'Orange'} %}
{% set myHash  = {'first': 1, 'second': 4/2, 'third': '3'} %}

計算

次の演算子を使用して、テンプレートで計算を行えます。

演算子 説明
+ 足し算を実行します。 {{ 2 + 3 }} returns 5
- Perform a substraction operation {{ 2 - 3 }} returns -1
* Perform a multiplication operation {{ 2 * 3 }} returns 6
/ Perform a division operation {{ 10 / 2 }} returns 5
% Calculate the remainder of an integer division {{ 10 % 3 }} returns 1

比較

次の比較演算が使用できます。

演算子 説明
== 両方のオペランドが等しいかどうかをチェックする
!= 両方のオペランドが等しくないかをチェックする
<> 両方のオペランドが等しくないかをチェックする
> 左オペランドが右オペランドより大きいかどうかをチェックする
< 左オペランドが右オペランドより小さいかどうかをチェックする
<= 左オペランドが右オペランドより小さい、もしくは等しいかどうかをチェックする
>= 左オペランドが右オペランドより大きい、もしくは等しいかどうかをチェックする
=== 両方のオペランドが同一かどうかをチェックする
!== 両方のオペランドが同一では無いかをチェックする

論理演算子

論理演算子は、複数のテストを組合せたif 式で使用します。

演算子 説明
or 左または右のオペランドがtrueと評価された場合はtrueを返します。
and 左と右の両方のオペランドがtrueと評価された場合はtrueを返します。
not 式を否定します
( expr ) 式のグルーピング

その他演算子

以下の追加の演算子が利用できます。

演算子 説明
~ Concatenates both operands {{ 'hello ' ~ 'world' }}
| Applies a filter in the right operand to the left {{ 'hello'|uppercase }}
.. Creates a range {{ 'a'..'z' }} {{ 1..10 }}
is ==(equals)と同じですが、テストも実行します
in 式が他の式に含まれている事をチェックする if 'a' in 'abc'
is not != (not equals) と同じ
'a' ? 'b' : 'c' 三項演算子。 PHPの三項演算子と同じ
++ 値を増やす
-- 値を減らす

演算子を使用方法を示します。


{% set robots = ['Voltron', 'Astro Boy', 'Terminator', 'C3PO'] %}

{% for index in 0..robots|length %}
    {% if robots[index] is defined %}
        {{ 'Name: ' ~ robots[index] }}
    {% endif %}
{% endfor %}

テスト

Tests can be used to test if a variable has a valid expected value. The operator is is used to perform the tests:


{% set robots = ['1': 'Voltron', '2': 'Astro Boy', '3': 'Terminator', '4': 'C3PO'] %}

{% for position, name in robots %}
    {% if position is odd %}
        {{ name }}
    {% endif %}
{% endfor %}

Voltで使用できるビルトインのテスト:

テスト 説明
defined 変数が定義されているかどうかをチェックします (isset())
divisibleby 値が他の値で割り切れるかどうかをチェックします
empty 変数が空であるかどうかをチェックします
even 数値が偶数であるかどうかをチェックします。
iterable 値が反復可能かどうかをチェックします。 ‘for’ 文で取得できます
numeric 値が数値かどうかをチェックします
odd 数値が奇数かどうかをチェックします
sameas 値が他の値と等しいかどうかをチェックします
scalar 値がスカラー(配列またはオブジェクトではない)かどうかをチェックします
type 値が指定された型かどうかをチェックします

その他の例


{% if robot is defined %}
    The robot variable is defined
{% endif %}

{% if robot is empty %}
    The robot is null or isn't defined
{% endif %}

{% for key, name in [1: 'Voltron', 2: 'Astroy Boy', 3: 'Bender'] %}
    {% if key is even %}
        {{ name }}
    {% endif %}
{% endfor %}

{% for key, name in [1: 'Voltron', 2: 'Astroy Boy', 3: 'Bender'] %}
    {% if key is odd %}
        {{ name }}
    {% endif %}
{% endfor %}

{% for key, name in [1: 'Voltron', 2: 'Astroy Boy', 'third': 'Bender'] %}
    {% if key is numeric %}
        {{ name }}
    {% endif %}
{% endfor %}

{% set robots = [1: 'Voltron', 2: 'Astroy Boy'] %}
{% if robots is iterable %}
    {% for robot in robots %}
        ...
    {% endfor %}
{% endif %}

{% set world = 'hello' %}
{% if world is sameas('hello') %}
    {{ 'it's hello' }}
{% endif %}

{% set external = false %}
{% if external is type('boolean') %}
    {{ 'external is false or true' }}
{% endif %}

マクロ

マクロは、テンプレート内のロジックを再利用するために使用できます。マクロは PHP関数として機能し、パラメータを受け取り、値を返すことができます。


{# Macro 'display a list of links to related topics' #}
{%- macro related_bar(related_links) %}
    <ul>
        {%- for link in related_links %}
            <li>
                <a href='{{ url(link.url) }}' title='{{ link.title|striptags }}'>
                    {{ link.text }}
                </a>
            </li>
        {%- endfor %}
    </ul>
{%- endmacro %}

{# Print related links #}
{{ related_bar(links) }}

<div>This is the content</div>

{# Print related links again #}
{{ related_bar(links) }}

マクロを呼び出すとき、パラメーターは名前渡しです:


{%- macro error_messages(message, field, type) %}
    <div>
        <span class='error-type'>{{ type }}</span>
        <span class='error-field'>{{ field }}</span>
        <span class='error-message'>{{ message }}</span>
    </div>
{%- endmacro %}

{# Call the macro #}
{{ error_messages('type': 'Invalid', 'message': 'The name is invalid', 'field': 'name') }}

マクロは値を返します:


{%- macro my_input(name, class) %}
    {% return text_field(name, 'class': class) %}
{%- endmacro %}

{# Call the macro #}
{{ '<p>' ~ my_input('name', 'input-text') ~ '</p>' }}

またオプションのパラメーターを受け取ります:


{%- macro my_input(name, class='input-text') %}
    {% return text_field(name, 'class': class) %}
{%- endmacro %}

{# Call the macro #}
{{ '<p>' ~ my_input('name') ~ '</p>' }}
{{ '<p>' ~ my_input('name', 'input-text') ~ '</p>' }}

タグヘルパーを使用する

Volt is highly integrated with Phalcon\Tag, so it’s easy to use the helpers provided by that component in a Volt template:


{{ javascript_include('js/jquery.js') }}

{{ form('products/save', 'method': 'post') }}

    <label for='name'>Name</label>
    {{ text_field('name', 'size': 32) }}

    <label for='type'>Type</label>
    {{ select('type', productTypes, 'using': ['id', 'name']) }}

    {{ submit_button('Send') }}

{{ end_form() }}

以下のPHPが生成できます。

<?php echo Phalcon\Tag::javascriptInclude('js/jquery.js') ?>

<?php echo Phalcon\Tag::form(array('products/save', 'method' => 'post')); ?>

    <label for='name'>Name</label>
    <?php echo Phalcon\Tag::textField(array('name', 'size' => 32)); ?>

    <label for='type'>Type</label>
    <?php echo Phalcon\Tag::select(array('type', $productTypes, 'using' => array('id', 'name'))); ?>

    <?php echo Phalcon\Tag::submitButton('Send'); ?>


{{ end_form() }}

To call a Phalcon\Tag helper, you only need to call an uncamelized version of the method:

メソッド Volt関数
Phalcon\Tag::checkField check_field
Phalcon\Tag::dateField date_field
Phalcon\Tag::emailField email_field
Phalcon\Tag::endForm end_form
Phalcon\Tag::fileField file_field
Phalcon\Tag::form form
Phalcon\Tag::friendlyTitle friendly_title
Phalcon\Tag::getTitle get_title
Phalcon\Tag::hiddenField hidden_field
Phalcon\Tag::image image
Phalcon\Tag::javascriptInclude javascript_include
Phalcon\Tag::linkTo link_to
Phalcon\Tag::numericField numeric_field
Phalcon\Tag::passwordField password_field
Phalcon\Tag::radioField radio_field
Phalcon\Tag::select select
Phalcon\Tag::selectStatic select_static
Phalcon\Tag::stylesheetLink stylesheet_link
Phalcon\Tag::submitButton submit_button
Phalcon\Tag::textArea text_area
Phalcon\Tag::textField text_field

関数

Voltで使用できるビルトインの関数:

名前 説明
content 以前のレンダリング段階で作成されたコンテンツが含まれます
get_content content と同じです
partial 現在のテンプレートにパーシャルビューを動的にロードする
super 親ブロックの内容をレンダリングする
time 同じ名前のPHP関数を呼び出します
date 同じ名前のPHP関数を呼び出します
dump PHP関数を呼び出します var_dump()
version フレームワークの現在のバージョンを返します
constant PHP定数を読み込む
url ‘url’ サービスを使用してURLを生成する

Viewとの連携

Also, Volt is integrated with Phalcon\Mvc\View, you can play with the view hierarchy and include partials as well:


{{ content() }}

<!-- Simple include of a partial -->
<div id='footer'>{{ partial('partials/footer') }}</div>

<!-- Passing extra variables -->
<div id='footer'>{{ partial('partials/footer', ['links': links]) }}</div>

パーシャルはランタイムに含まれています。Voltはincludeを提供しており、これはビューのコンテンツをコンパイルし、インクルードされたビューのパーツとしてそのコンテンツを返します:


{# Simple include of a partial #}
<div id='footer'>
    {% include 'partials/footer' %}
</div>

{# Passing extra variables #}
<div id='footer'>
    {% include 'partials/footer' with ['links': links] %}
</div>

include文

Voltを使う上で、include は性能を改善するために特別な働きをします。ファイルをインクルードするときにこの拡張モジュールを指定した場合、このテンプレートをコンパイルしたときにこの拡張モジュールがあった場合、 それがインクルードされる親テンプレート中にそのテンプレートの内容をインライン化できます。 ただしincludewithで渡された変数を持っている場合、テンプレートはインライン化されません。


{# The contents of 'partials/footer.volt' is compiled and inlined #}
<div id='footer'>
    {% include 'partials/footer.volt' %}
</div>

partial文 vs include文

partial関数やincludeを使用するときは、以下の点に注意してください:

タイプ 説明
partial Voltと他のテンプレートエンジンで作成されたテンプレートをインクルードすることができます
  他のビューのコンテンツを動的に含めることができる、変数のような式を渡すことができます
  含める必要があるコンテンツが頻繁に変更される場合はベスト
includes コンパイルされたコンテンツをビューにコピーしてパフォーマンスを向上させます
  Voltで作成したテンプレートのみインクルードできます
  コンパイル時に既存のテンプレートが必要です

テンプレートの継承

テンプレートの継承を使用すると、他のテンプレートで拡張してコードを再利用できる基本テンプレートを作成できます。 ベーステンプレートは子テンプレートによって上書きできるblocksを定義します。 次のベーステンプレートがあるとしましょう:


{# templates/base.volt #}
<!DOCTYPE html>
<html>
    <head>
        {% block head %}
            <link rel='stylesheet' href='style.css' />
        {% endblock %}

        <title>{% block title %}{% endblock %} - My Webpage</title>
    </head>

    <body>
        <div id='content'>{% block content %}{% endblock %}</div>

        <div id='footer'>
            {% block footer %}&copy; Copyright 2015, All rights reserved.{% endblock %}
        </div>
    </body>
</html>

他のテンプレートからは、ブロックを置き換えて基本テンプレートを拡張することができます:


{% extends 'templates/base.volt' %}

{% block title %}Index{% endblock %}

{% block head %}<style type='text/css'>.important { color: #336699; }</style>{% endblock %}

{% block content %}
    <h1>Index</h1>
    <p class='important'>Welcome on my awesome homepage.</p>
{% endblock %}

Not all blocks must be replaced at a child template, only those that are needed. The final output produced will be the following:

<!DOCTYPE html>
<html>
    <head>
        <style type='text/css'>.important { color: #336699; }</style>

        <title>Index - My Webpage</title>
    </head>

    <body>
        <div id='content'>
            <h1>Index</h1>
            <p class='important'>Welcome on my awesome homepage.</p>
        </div>

        <div id='footer'>
            &copy; Copyright 2015, All rights reserved.
        </div>
    </body>
</html>

多重継承

Extended templates can extend other templates. The following example illustrates this:


{# main.volt #}
<!DOCTYPE html>
<html>
    <head>
        <title>Title</title>
    </head>

    <body>
        {% block content %}{% endblock %}
    </body>
</html>

layout.volt テンプレートで main.volt を拡張します。


{# layout.volt #}
{% extends 'main.volt' %}

{% block content %}

    <h1>Table of contents</h1>

{% endblock %}

最終的にlayout.voltを拡張したビューは次のようになります:


{# index.volt #}
{% extends 'layout.volt' %}

{% block content %}

    {{ super() }}

    <ul>
        <li>Some option</li>
        <li>Some other option</li>
    </ul>

{% endblock %}

index.volt のレンダリングは次のようになります:

<!DOCTYPE html>
<html>
    <head>
        <title>Title</title>
    </head>

    <body>

        <h1>Table of contents</h1>

        <ul>
            <li>Some option</li>
            <li>Some other option</li>
        </ul>

    </body>
</html>

関数 super() の呼び出しに注意してください。 その関数がある場合、親ブロックの内容をレンダリングできます。 partials のように、extendsに設定されているパスは、現在の Viewディレクトリからの相対パスになります。(つまり app/views/です。)

デフォルトでは、パフォーマンス上の理由から、Voltは子テンプレートの変更をチェックして、プレーンなPHPにいつ再コンパイルするかを知るため、オプション 'compileAlways' => true でVoltを初期化することをお勧めします。 したがって、テンプレートは常に親テンプレートの変更を考慮してコンパイルされます。

自動エスケープ モード

自動エスケープモードを使用して、ブロックに出力されたすべての変数の自動エスケープを有効にすることができます:


Manually escaped: {{ robot.name|e }}

{% autoescape true %}
    Autoescaped: {{ robot.name }}
    {% autoescape false %}
        No Autoescaped: {{ robot.name }}
    {% endautoescape %}
{% endautoescape %}

Volt の拡張

他のテンプレートエンジンとは異なり、Volt自体はコンパイルされたテンプレートを実行する必要はありません。 テンプレートがコンパイルされると、Voltには依存しません。 パフォーマンスの独立性を念頭において、VoltはPHPテンプレート用のコンパイラとしてのみ機能します。

Voltコンパイラでは、関数、テスト、フィルタを追加して既存のものに追加することができます。

関数

関数は通常のPHP関数として機能し、関数名としては有効な文字列名が必要です。 関数は、単純な文字列を返すか、または無名関数を使用する2つの方法を使用して追加できます。 選択した方法で、常に有効なPHP文字列式を返すことが必要です。

<?php

use Phalcon\Mvc\View\Engine\Volt;

$volt = new Volt($view, $di);

$compiler = $volt->getCompiler();

// これは、Voltの関数名 'shuffle'をPHP関数 'str_shuffle'にバインドします
$compiler->addFunction('shuffle', 'str_shuffle');

Register the function with an anonymous function. This case we use $resolvedArgs to pass the arguments exactly as were passed in the arguments:

<?php

$compiler->addFunction(
    'widget',
    function ($resolvedArgs, $exprArgs) {
        return 'MyLibrary\Widgets::get(' . $resolvedArgs . ')';
    }
);

個別で未解決の引数を扱う:

<?php

$compiler->addFunction(
    'repeat',
    function ($resolvedArgs, $exprArgs) use ($compiler) {
        // 最初の引数を解決する
        $firstArgument = $compiler->expression($exprArgs[0]['expr']);

        // 2番目の引数が渡されたかどうかをチェックする
        if (isset($exprArgs[1])) {
            $secondArgument = $compiler->expression($exprArgs[1]['expr']);
        } else {
            // デフォルトで '10' を使用
            $secondArgument = '10';
        }

        return 'str_repeat(' . $firstArgument . ', ' . $secondArgument . ')';
    }
);

いくつかの機能の可用性に基づいてコードを生成する:

<?php

$compiler->addFunction(
    'contains_text',
    function ($resolvedArgs, $exprArgs) {
        if (function_exists('mb_stripos')) {
            return 'mb_stripos(' . $resolvedArgs . ')';
        } else {
            return 'stripos(' . $resolvedArgs . ')';
        }
    }
);

組み込み関数をオーバーライドして、その関数の名前を追加することができます:

<?php

// 組み込み関数のdumpを置き換える
$compiler->addFunction('dump', 'print_r');

フィルター

A filter has the following form in a template: leftExpr name(optional-args). Adding new filters is similar as seen with the functions:
<?php

// これにより、PHP関数 'md5'を使用するフィルタ 'hash' が作成されます。
$compiler->addFilter('hash', 'md5');
<?php

$compiler->addFilter(
    'int',
    function ($resolvedArgs, $exprArgs) {
        return 'intval(' . $resolvedArgs . ')';
    }
);

組み込みフィルターをオーバーライドして、その関数の名前を追加することができます:

<?php

// 組み込みフィルタを置き換える 'capitalize'
$compiler->addFilter('capitalize', 'lcfirst');

拡張

拡張機能を使用すると、開発者はテンプレートエンジンを拡張したり、特定の命令のコンパイルをオーバーライドしたり、式や演算子の動作を変更したり、関数やフィルタを追加したりすることができます。

An extension is a class that implements the events triggered by Volt as a method of itself. For example, the class below allows to use any PHP function in Volt:

<?php

class PhpFunctionExtension
{
    /**
     * このメソッドは、関数呼び出しをコンパイルしようとすると呼び出されます
     */
    public function compileFunction($name, $arguments)
    {
        if (function_exists($name)) {
            return $name . '('. $arguments . ')';
        }
    }
}

上記のクラスは、どのテンプレートでも関数呼び出しをコンパイルしようとする前に呼び出されるメソッド compileFunction を実装しています。 拡張の目的は、コンパイルされる関数がテンプレートからPHP関数を呼び出すことができるかどうかを検証することです。 拡張機能のイベントは、有効なPHPコードを返す必要があります。これは、Voltによって生成されたものの代わりにコンパイルの結果として使用されます。 イベントが文字列を返さない場合、コンパイルはエンジンによって提供されるデフォルトの動作を使用して行われます。

エクステンションに実装できるコンパイルイベントは次のとおりです:

イベント/関数 説明
compileFunction テンプレート内の任意の関数呼び出しをコンパイルしようとする前にトリガされる
compileFilter テンプレート内の任意のフィルター呼び出しをコンパイルしようとする前にトリガされる
resolveExpression 任意の式をコンパイルする前にトリガされます。 これにより、開発者は演算子をオーバーライドできます
compileStatement 任意の式をコンパイルする前にトリガされます。 これにより、開発者は式をオーバーライドできます

Volt拡張はコンパイラに登録して、コンパイル時に利用できるようにする必要があります:

<?php

// コンパイラに拡張機能を登録する
$compiler->addExtension(
    new PhpFunctionExtension()
);

Viewの断片のキャッシュ

With Volt it’s easy cache view fragments. This caching improves performance preventing that the contents of a block from being executed by PHP each time the view is displayed:


{% cache 'sidebar' %}
    <!-- generate this content is slow so we are going to cache it -->
{% endcache %}

特定の秒数を設定する:


{# cache the sidebar by 1 hour #}
{% cache 'sidebar' 3600 %}
    <!-- generate this content is slow so we are going to cache it -->
{% endcache %}

任意の有効な式をキャッシュキーとして使用できます:


{% cache ('article-' ~ post.id) 3600 %}

    <h1>{{ post.title }}</h1>

    <p>{{ post.content }}</p>

{% endcache %}

キャッシングは、ビューコンポーネントを介して Phalcon\Cache コンポーネントによって行われます。 Learn more about how this integration works in the section Caching View Fragments.

テンプレートへのサービス注入

サービスコンテナ(DI)がVoltで使用可能な場合は、テンプレート内のサービス名にアクセスするだけでサービスを使用できます:


{# Inject the 'flash' service #}
<div id='messages'>{{ flash.output() }}</div>

{# Inject the 'security' service #}
<input type='hidden' name='token' value='{{ security.getToken() }}'>

独立コンポーネント

スタンドアロンモードでVoltを使用すると、次のようになります:

<?php

use Phalcon\Mvc\View\Engine\Volt\Compiler as VoltCompiler;

// コンパイラーを生成
$compiler = new VoltCompiler();

// オプションでいくつかのオプションを追加する
$compiler->setOptions(
    [
        // ...
    ]
);

// PHPコードを返すテンプレート文字列をコンパイルする
echo $compiler->compileString(
    "hello"
);

// コピー先のファイルを指定してファイル内のテンプレートをコンパイルする
$compiler->compileFile(
    'layouts/main.volt',
    'cache/layouts/main.volt.php'
);

// コンパイラに渡されるオプションに基づいてファイル内のテンプレートをコンパイルする
$compiler->compile(
    'layouts/main.volt'
);

// コンパイルされたテンプレートをrequire(オプション)
require $compiler->getCompiledTemplatePath();

外部リソース

  • Sublime/Textmate用のバンドルは こちら
  • Phosphorum PhalconのフォーラムではもちろんVoltを使っています。 Github
  • Vökuró その他のサンプルアプリケーションはVoltを利用しています Github