mustacheはとてもシンプルなテンプレートで、多くの言語で処理エンジンが実装されています。
SmartyやJinjaのように多くの機能はありませんが、その分すっきりしたテンプレートになります。
今回はPHPでmustacheを使ってみます。
インストールと準備
そのまま落としてもよいのですが、composerから落とします。以下のcomposer.jsonを作ってcomposer.pharを実行します。
composer.json
1 2 3 4 5 |
{ "require": { "mustache/mustache": "*" } } |
1 2 3 4 5 6 7 8 |
$ composer.phar install Loading composer repositories with package information Installing dependencies (including require-dev) - Installing mustache/mustache (v2.4.1) Loading from cache Writing lock file Generating autoload files |
以下のディレクトリを作ります。
ディレクトリ | 説明 |
---|---|
public | DocumentRoot |
views | テンプレートの置き場所 |
views/partials | テンプレートの部品の置き場所 |
cache | キャッシュの保存場所 ※Webサーバーからの書き込みを許可してください。 |
ここまでで以下のディレクトリ構成になります。
1 2 3 4 5 6 7 8 9 10 11 12 |
tree -L 2 . ├── cache ├── composer.json ├── composer.lock ├── public ├── vendor │ ├── autoload.php │ ├── composer │ ├── mustache └── views └── partials |
テンプレートの読み込み
プレースホルダのないテンプレートを作ってPHPから読み込んでみます。以下のファイルを作って、index.phpにアクセスしてindex.mustacheがそのまま出力されればOKです。
views/index.mustache
1 2 3 4 5 6 7 8 9 10 11 |
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>mustache</title> </head> <body> <h1>読み込みテスト</h1> </div> </body> </html> |
public/index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php require_once(dirname(__FILE__).'/../vendor/autoload.php'); $m = new Mustache_Engine(array( 'cache' => dirname(__FILE__).'/../cache', 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/../views'), 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/../views/partials'), 'escape' => function($v) { return htmlspecialchars($v, ENT_QUOTES, 'UTF-8'); }, )); $tpl = $m->loadTemplate('index'); echo $tpl->render(); ?> |
ここで指定したコンストラクタのオプションは、(1)上で作ったディレクトリの指定、(2)テンプレートのロードの方法、(3)特殊文字のエスケープの方法です。テンプレートのロードは、ほとんどの場合、ここで使っているFilesystemLoaderを使うことになると思います。詳細およびその他のオプションは、マニュアルを参照してください。
テンプレートのロード(loadTemplate)の引数は、テンプレートファイルから拡張子を取ったものを指定します。また、テンプレートファオルダにサブフォルダを作ることも可能で、呼び出しは “sub/index” のようにします。
テンプレートの基本
HTMLのテンプレートには少なくても、(1)変数を展開する位置を指定する方法と(2)リストやテーブルを生成するための繰り返し箇所を指定する方法が必要になります。
PHPで展開する変数を1つの配列に詰めて、テンプレートに展開するという方法をとりますが、長々説明するより実例を見たほうが分かりやすいと思います。
views/index.mustache
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>mustache</title> </head> <body> <h1>{{corp}}</h1> <table> <thead> <tr> <th>氏名</th> <th>部署</th> <th>役職</th> </tr> </thead> <tbody> {{# emp}} <tr> <td>{{name}}</td> <td>{{div}}</td> <td>{{title}}</td> </tr> {{/ emp}} </tbody> </table> </div> </body> </html> |
public/index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php require_once(dirname(__FILE__).'/../vendor/autoload.php'); $m = new Mustache_Engine(array( 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/../views'), 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/../views/partials'), 'escape' => function($v) { return htmlspecialchars($v, ENT_QUOTES, 'UTF-8'); }, )); $v = array( 'corp' => '○✕商事', 'emp' => array( array('name'=>'山田太郎', 'div'=>'営業部', 'title'=>'部長'), array('name'=>'鈴木花子', 'div'=>'広報部', 'title'=>'係長'), array('name'=>'小林二郎', 'div'=>'開発部', 'title'=>''), ), ); $tpl = $m->loadTemplate('index'); echo $tpl->render($v); ?> |
展開結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>mustache</title> </head> <body> <h1>○✕商事</h1> <table> <thead> <tr> <th>氏名</th> <th>部署</th> <th>役職</th> </tr> </thead> <tbody> <tr> <td>山田太郎</td> <td>営業部</td> <td>部長</td> </tr> <tr> <td>鈴木花子</td> <td>広報部</td> <td>係長</td> </tr> <tr> <td>小林二郎</td> <td>開発部</td> <td></td> </tr> </tbody> </table> </div> </body> </html> |
下の配列のように”corp”というキーが重複した場合は、繰り返し部分の中では、内側のものが使われます。
1 2 3 4 5 6 7 8 |
$v = array( 'corp' => '○✕商事', 'emp' => array( array('name'=>'山田太郎', 'div'=>'営業部', 'title'=>'部長', 'corp'=>'△□機械'), array('name'=>'鈴木花子', 'div'=>'広報部', 'title'=>'係長'), array('name'=>'小林二郎', 'div'=>'開発部', 'title'=>''), ), ); |
展開結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>mustache</title> </head> <body> <h1>○✕商事</h1> <table> <thead> <tr> <th>氏名</th> <th>部署</th> <th>役職</th> </tr> </thead> <tbody> <tr> <td>山田太郎</td> <td>△□機械営業部</td> <td>部長</td> </tr> <tr> <td>鈴木花子</td> <td>○✕商事広報部</td> <td>係長</td> </tr> <tr> <td>小林二郎</td> <td>○✕商事開発部</td> <td></td> </tr> </tbody> </table> </div> </body> </html> |
もちろん、配列は入れ子にもできます。
views/index.mustache
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>mustache</title> </head> <body> <h1>{{corp}}</h1> <table> <thead> <tr> <th>氏名</th> <th>部署</th> <th>役職</th> <th>家族</th> </tr> </thead> <tbody> {{# emp}} <tr> <td>{{name}}</td> <td>{{corp}}{{div}}</td> <td>{{title}}</td> <td> <ul> {{# family}} <li>{{name}}</li> {{/ family}} </ul> </td> </tr> {{/ emp}} </tbody> </table> </div> </body> </html> |
public/index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$v = array( 'corp' => '○✕商事', 'emp' => array( array( 'name'=>'山田太郎', 'div'=>'営業部', 'title'=>'部長', 'family'=> array( array('name'=>'一郎'), array('name'=>'裕太'), array('name'=>'ゆり'), ), ), array('name'=>'鈴木花子', 'div'=>'広報部', 'title'=>'係長'), array('name'=>'小林二郎', 'div'=>'開発部', 'title'=>''), ), ); |
展開結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>mustache</title> </head> <body> <h1>○✕商事</h1> <table> <thead> <tr> <th>氏名</th> <th>部署</th> <th>役職</th> <th>家族</th> </tr> </thead> <tbody> <tr> <td>山田太郎</td> <td>○✕商事営業部</td> <td>部長</td> <td> <ul> <li>一郎</li> <li>裕太</li> <li>ゆり</li> </ul> </td> </tr> <tr> <td>鈴木花子</td> <td>○✕商事広報部</td> <td>係長</td> <td> <ul> </ul> </td> </tr> <tr> <td>小林二郎</td> <td>○✕商事開発部</td> <td></td> <td> <ul> </ul> </td> </tr> </tbody> </table> </div> </body> </html> |
変数は、コンストラクタの’escape’オプションで定義した関数でエスケープされますが、エスケープしたくない場合は、{{{val}}}もしくは{{& val}}とします。
フィルタ
SmartyやJinjaのようにテンプレート側の指定方法がスマートではありませんが、フィルタを書くこともできます。
views/index.mustache
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>mustache</title> </head> <body> <h1>{{corp}}</h1> <table> <thead> <tr> <th>氏名</th> <th>部署</th> <th>役職</th> <th>家族</th> </tr> </thead> <tbody> {{# emp}} <tr> <td>{{name}}</td> <td>{{corp}}{{div}}</td> <td>{{title}}</td> <td> <ul> {{# family}} <li>{{name}}{{# wrapped}}{{age}}{{/ wrapped}}</li> {{/ family}} </ul> </td> </tr> {{/ emp}} </tbody> </table> </div> </body> </html> |
public/index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php require_once(dirname(__FILE__).'/../vendor/autoload.php'); $m = new Mustache_Engine(array( // 'cache' => dirname(__FILE__).'/../cache', 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/../views'), 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/../views/partials'), 'escape' => function($v) { return htmlspecialchars($v, ENT_QUOTES, 'UTF-8'); }, )); $v = array( 'corp' => '○✕商事', 'emp' => array( array( 'name'=>'山田太郎', 'div'=>'営業部', 'title'=>'部長', 'family'=> array( array('name'=>'一郎', 'age'=>15), array('name'=>'裕太', 'age'=>10), array('name'=>'ゆり', 'age'=>40), ), 'wrapped'=>function($text){ return "(" . $text . ")"; } ), array('name'=>'鈴木花子', 'div'=>'広報部', 'title'=>'係長'), array('name'=>'小林二郎', 'div'=>'開発部', 'title'=>''), ), ); $tpl = $m->loadTemplate('index'); echo $tpl->render($v); ?> |
展開結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>mustache</title> </head> <body> <h1>○✕商事</h1> <table> <thead> <tr> <th>氏名</th> <th>部署</th> <th>役職</th> <th>家族</th> </tr> </thead> <tbody> <tr> <td>山田太郎</td> <td>○✕商事営業部</td> <td>部長</td> <td> <ul> <li>一郎(15)</li> <li>裕太(10)</li> <li>ゆり(40)</li> </ul> </td> </tr> <tr> <td>鈴木花子</td> <td>○✕商事広報部</td> <td>係長</td> <td> <ul> </ul> </td> </tr> <tr> <td>小林二郎</td> <td>○✕商事開発部</td> <td></td> <td> <ul> </ul> </td> </tr> </tbody> </table> </div> </body> </html> |
部分テンプレートの利用
ヘッダ、フッタ、メニューのように複数ページに渡って使用する部分を部品として独立したテンプレートにして使用することができます。
部品にする部分は、コンストラクタの”partials_loader”オプションで指定したフォルダに入れます。
views/partials/header.mustach
1 2 |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>mustache</title> |
views/index.mustache
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<!DOCTYPE html> <html lang="ja"> <head> {{> header}} </head> <body> <h1>{{corp}}</h1> <table> <thead> <tr> <th>氏名</th> <th>部署</th> <th>役職</th> <th>家族</th> </tr> </thead> <tbody> {{# emp}} <tr> <td>{{name}}</td> <td>{{corp}}{{div}}</td> <td>{{title}}</td> <td> <ul> {{# family}} <li>{{name}}{{# wrapped}}{{age}}{{/ wrapped}}</li> {{/ family}} </ul> </td> </tr> {{/ emp}} </tbody> </table> </div> </body> </html> |
部分テンプレートにもタグを組み込めます。主テンプレートに部分プレートを組み込んだ後に変数展開が行われます。
views/partials/header.mustache
1 2 |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>{{title}}</title> |
public/index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$v = array( 'title' => 'mustache', 'corp' => '○✕商事', 'emp' => array( array( 'name'=>'山田太郎', 'div'=>'営業部', 'title'=>'部長', 'family'=> array( array('name'=>'一郎', 'age'=>15), array('name'=>'裕太', 'age'=>10), array('name'=>'ゆり', 'age'=>40), ), 'wrapped'=>function($text){ return "(" . $text . ")"; } ), array('name'=>'鈴木花子', 'div'=>'広報部', 'title'=>'係長'), array('name'=>'小林二郎', 'div'=>'開発部', 'title'=>''), ), ); |
機能が少ないですが、HTMLのテンプレートとしては順分使えます。