[PHP]Twigテンプレートの実践的な構成と作り方

Twig は Silex のビューとして用いられているテンプレートエンジンです。
基本的な扱い方やインストール方法などについては前回の記事で取り扱ったので、今回はより実践的なテンプレートの組み方について説明します。

extends

一般的なサイトはヘッダー、フッター、サイドバー、メインコンテンツで構成されており、メインコンテンツ以外の場合は共通のものを使うケースが多いです。そのような場合 Twig では extends で土台となるテンプレートを利用します。ビューフォルダの中に「layouts」などの名前でフォルダを作り、base.twig というファイルを追加します。

layouts/base.twig

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>{% block title %}Twig{% endblock %}</title>
    </head>
    <body>
    <div id="header"></div>

    {% block content %}{% endblock %}

    <div id="footer"></div>
    </body>
</html>

base.twig にはメインコンテンツ以外の部分を記述しました。{% block ブロック名 %}〜{% endblock %} となっている部分にメインコンテンツが入ります。ブロック名は目的に合わせて好きな名前をつけることが出来ます。
ではこの base.twig を土台にトップページである index.twig をビューフォルダ内に作ります。(layouts フォルダ内ではありません)

index.twig

{% extends 'layouts/base.twig' %}

{% block title %}Home{% endblock %}

{% block content %}
<h1>Hello, World!</h1>
{% endblock %}

{% extends 'ファイルの場所' %} と書くことで土台とするテンプレートを指定します。
{% block ブロック名 %} と {% endblock %} で挟まれた内容が base.twig の block 部分はこの内容に置き換えられ、結果として次の HTML が出力されます。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Home</title>
    </head>
    <body>
    <div id="header"></div>

    <h1>Hello, World!</h1>

    <div id="footer"></div>
    </body>
</html>

ブロック名「title」の部分は「Home」という文字に変わり、「content」の部分は「Hello, World!」で置き換えられています。
ブロックは省略することができ、省略した場合は親となるテンプレートの {% block ブロック名 %} と {% endblock %} で挟まれた内容が初期値として表示されます。例では親テンプレートに {% block title %}Twig{% endblock %} のようにして「Twig」というタイトルが初期値として用意されているため、小テンプレート index.twig で {% block title %} の箇所を削除した場合はタイトルとしてデフォルトの「Twig」という文字が使われることになります。

include

{% include 'ファイルの場所' %} を用いると他のテンプレートファイルを読み込むことが出来ます。
ヘッダー部分(id=header)とフッター部分(id=footer)をそれぞれ header.twig、footer.twig というファイルに切り分け、layouts フォルダに保存した上でそれらを base.twig から読み込んでみます。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>{% block title %}Twig{% endblock %}</title>
    </head>
    <body>
    {% include 'layouts/header.twig' %}

    {% block content %}{% endblock %}

    {% include 'layouts/footer.twig' %}
    </body>
</html>

出力される HTML は先程のものと同じです。このようにしてテンプレートファイルを分割しておくと、ホームページのみヘッダーを変えるなどの場合に便利です。

変数

コントローラーから渡された変数は {{ 変数名 }} の形で表示することが出来ます。PHP のように変数名の頭に「$」をつけることはなく、{{ var }} のようにします。
この方法で出力される値は <> などが適切に変換(エスケープ)された安全な状態で出力されます。
エスケープを行わずに出力する場合は filter の raw を使い、{{ 変数名|raw }} とすることでそのままの値を表示できます。filter には number_format や nl2br などの一般的な関数が多数用意されています。通常は必要ありませんが再度エスケープし直す場合は escape や短縮形の e というフィルターも用意されています。
また、「|」で区切ることで複数のフィルターをつなげて使うことも出来ます。

変数や文字列を連結する場合「~」(チルダ)を用いて繋ぎます。PHPと異なるので気をつけて下さい。

変数は基本的にコントローラーで用意されますが、ビュー側で set を使うことで変数を定義することも出来ます

{% set 変数名 = 値 %}

値が複数行の場合 {% set %}〜{% endset %} を使うことも出来ます

{% set foo %}
line 1
line 2
line 3
{% endset %}

配列は PHP 同様 [1, 2, 3] のように表現しますが、連想配列の場合波括弧を用いて {'foo': 1, 'bar': 2} のようにする必要があります。

制御構文

ループ

Twig でループを作るには for 〜 in を用います。扱いは foreach と似ていますが順番に注意して下さい。

<ul>
    {% for user in users %}
        <li>{{ user.name }}</li>
    {% endfor %}
</ul>

{% for key, value in users %} のようにしてキー名を取り出すことも出来ます。

PHP の for 近い使い方をする場合は次のように書きます。この例は 0〜10 までの数字を出力します。

<ul>
{% for i in 0..10 %}
    <li>{{ i }}</li>
{% endfor %}
</ul>

ループ内では loop というオブジェクト変数が自動的に用意され、次の値を得ることが出来ます

loop.index ループ回数(1から始まる)
loop.index0 ループ回数(0から始まる)
loop.revindex ループ回数の逆順(1で終わる)
loop.revindex0 ループ回数の逆順(0で終わる)
loop.first ループが最初のとき true を返す
loop.last ループが最後のとき true を返す
loop.length 配列の長さ
loop.parent ループがネスト(入れ子)しているときの親の loop を取得

条件分岐

条件分岐には {% if 条件 %}〜{% endif %} が利用できます。
PHP 同様、{% else %} や {% elseif %} も用意されています。

{% if foo == true %}
    <p>foo</p>
{% else %}
    <p>bar</p>
{% endif %}

複合条件の場合「and」と「or」が使えます。

{% if foo == 1 and bar == 2 %}

その他の機能

Twig でコメントを 使う場合 {# 〜 #} を使います。これは HTML には出力されません。
また、複数行でも利用できます。

{# comment here #}

変数の内容を確認する場合のために dump() が用意されています。これは PHP の関数 var_dump() と同じもので、変数の詳細を表示できます。

{{ dump(foo) }}

[PHP]Silexの基本的なルーティング方法とTwigによるビューの作り方

Silex は PHP 製のマイクロフレームワークで、EC-CUBE 3 のベースとしても利用されています。インストールには Composer が必要となりますのであらかじめインストールしておいて下さい。詳細は割愛しますが Linux, Unix, macOS であれば下記のようにしてインストールします。Windows の場合 exe 形式のインストーラーが公式に用意されています。

curl -s http://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

Silex 2.0のインストール

Silex のインストール方法は 2 種類あり、公式サイトからアーカイブファイルをダウンロードする方法と Composer 経由でインストールする方法があります。アーカイブファイルには必要最低限の slim 版と、全て入った fat 版がありますが、必要に応じてあとから追加していくこともできるので slim 版を使うのが良いと思います。今回は「silex-app」というフォルダ名で作業を進めていきますのでアーカイブ版を使う場合は展開してフォルダ名を変更して下さい。プロジェクトフォルダ名は自由につけても構いません。

Composer 経由でインストールする場合次のようにします。プロジェクト用のフォルダ「silex-app」を作り、そのフォルダ内にインストールします。(フォルダ名は一例ですので自由な名前をつけて読み替えて下さい。)

mkdir silex-app
cd silex-app
composer require silex/silex "~2.0"

これで Silex 本体はインストールされました。他にもテンプレートエンジンの Twig などが必要となりますので Composer で追加します。

composer require twig/twig
composer require symfony/twig-bridge

クラスファイルの自動読み込み(PSR-4)に対応するため、composer.json ファイルに手を加えます。
環境によってバージョンの違いはあると思いますが最終的に次のようになりました。

{
    "require": {
        "silex/silex": "~2.0",
        "twig/twig": "^1.24",
        "symfony/twig-bridge": "^3.1"
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        }
    }
}

書き足したら「composer dump-autoload」コマンドで更新しておきます。

コントローラー、ビューの作成

次に現在のプロジェクトフォルダに「app」と「web」の2つのフォルダを作ります。アーカイブ版をダウンロードした場合すでに「web」フォルダがあるかもしれません。「web」フォルダは Web 公開フォルダとなりますのでドメインの割り当てる際はこのフォルダをルートにして下さい。画像ファイルや CSS、JS ファイルもこのフォルダ以下に置きます。

フォルダを作成したら「web」フォルダ内に移動し、「index.php」ファイルと、「.htaccess」ファイル(Apacheの場合)を作成して下さい。
ファイルの内容は次のとおりです。

web\index.php

<?php
require_once __DIR__.'/../vendor/autoload.php';

$app = new Silex\Application();
$app['debug'] = true;
$app->get('/', function(){ return 'Hello, World!'; });

$app->run();

web\.htaccess

<IfModule mod_rewrite.c>
    Options -MultiViews

    RewriteEngine On
    #RewriteBase /silex-app/web
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [QSA,L]
</IfModule>

環境によっては RewriteBase を書き換える必要があります。# を外して適宜編集して下さい。

用意ができたらサイトにWebブラウザでアクセスします。ドメインを利用しない場合「http://localhost/silex-app/web」のようなアドレスになっていると思います。正しく動作している場合ブラウザに「Hello, World!」の文字が表示されます。

「$app->get()」となっている部分がルーティング処理です。「/」にアクセスしたときの動作を定義しています。
単純なプログラムでない限りは例のようにせず、ページコントローラーを経由してビューを表示することになります。
まずはコントローラーのためのフォルダを作成します。「app\Controllers」となるように作成して下さい。
その中にファイル「HomeController.php」を作成して下さい。

app\Controllers\HomeController.php

<?php
namespace App\Controllers;

use Silex\Application;
use Symfony\Component\HttpFoundation\Request;

class HomeController
{
    public function index(Application $app, Request $request){
        $data = ['foo' => 'World'];
        return $app['twig']->render('index.twig', $data);
    }
}

render() によって表示するビューファイルを指定し、第2引数でビュー内で使える変数を連想配列の形で渡します。必要なければ空でも構いません。
上記の例では変数 foo に 値「World」を格納してビューに渡しています。

次にビューとなる Twig ファイルを用意します。プロジェクトフォルダに「views」フォルダを作成し、その中に index.twig ファイルを作成します。

views\index.twig

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Silex</title>
    </head>
    <body>
        <h1>Hello, {{foo}}!</h1>
    </body>
</html>

基本的には HTML ファイルと変わりません。{{ 〜 }} を使うことで変数の内容などを表示します。この内容は自動的に安全な文字列に変換(エスケープ)されます。

コントローラーとビューが用意できたら Twig を使うための準備とルーティングを行います。再度 web\index.php を編集して下さい。twig.path には twig ファイルが格納されているフォルダのパスを指定します。

$app->register(new Silex\Provider\TwigServiceProvider(), [
    'twig.path' => __DIR__ . '/../views',
]);

$app->get('/', 'App\Controllers\HomeController::index')->bind('index');

get() の箇所を上のように書き換えます。これにより「/」にアクセスした際は HomeController のメソッド index() が実行されます。bind() によって一意な名前をつけておくと URL を生成する際に役に立ちます。ビューで下記のように名前を指定するとそのページへの URL が生成されます。

{{ url("index") }}

user/1234、post/1234 のようにユーザーIDや記事IDを URL からパラメータとして受け取る場合のルーティングは次のようにします。

$app->get('/post/{id}', 'App\Controllers\PostController::show')->assert('id', '\d+')->bind('post_show');

id は波括弧で括ります。assert() の正規表現によって id が数値のみである場合という条件にしています。
コントローラーでは次のようにして id の値を受け取れます。bind() ではアンダーバーで区切って「post_show」と名付けました。

app\Controllers\PostController.php

public function show(Application $app, Request $request, $id){
    //
}

引数 $id の中に ID の値が格納されます。この場合に Twig ビューで URL を生成するには次のようにします。

{{ url('post_show', {'id': 1234}) }}

コントローラーで「/?category=1」のような GET や POST パラメーターを受け取るには Request を使います。

$request->get('category');

以上が基本的な流れとなります。ここで紹介したクラス名やファイルの設置場所はあくまで例であり、どんな名前をつけるか、どこに置くかについてはアプリケーションに応じて変更して下さい。

Twigの詳細な書き方についてはこちらをお読み下さい。