HTML (Twig v2)
インストール
HTML表示のためにcomposerでTwig v2のモジュールをインストールします:
composer require madapaja/twig-module ^2.0
次にhtml
コンテキストファイルsrc/Module/HtmlModule.php
を用意してTwigModule
をインストールします:
namespace MyVendor\MyPackage\Module;
use Madapaja\TwigModule\TwigErrorPageModule;
use Madapaja\TwigModule\TwigModule;
use Ray\Di\AbstractModule;
class HtmlModule extends AbstractModule
{
protected function configure()
{
$this->install(new TwigModule);
$this->install(new TwigErrorPageModule);
}
}
TwigErrorPageModule
はエラー表示をHTMLで行うオプションです。HtmlModule
でインストールしないでProdModule
でインストールして開発時のエラー表示はJSONにすることもできます。
次にtemplates
フォルダをコピーします:
cp -r vendor/madapaja/twig-module/var/templates var/templates
bin/page.php
やpublic/index.php
のコンテキストを変更してhtml
を有効にします:
$context = 'cli-html-app'; // 'html-app'
テンプレート
1つのリソースクラスに1つのテンプレートファイルがvar/templates
フォルダに必要です。例えばsrc/Page/Index.php
にはvar/templates/Page/Index.html.twig
が必要です。テンプレートにリソースのbodyがアサインされます。
例)src/Page/Index.php
:
class Index extends ResourceObject
{
public $body = [
'greeting' => 'Hello BEAR.Sunday'
];
}
var/templates/Page/Index.twig.php
:
<h1></h1>
出力:
php bin/page.php get /
200 OK
content-type: text/html; charset=utf-8
<h1>Hello BEAR.Sunday</h1>
テンプレートファイルの選択
どのテンプレートを使用するかはリソースでは選択しません。リソースの状態によってinclude
します:
{% if user.is_login %}
{{ include('member.html.twig') }}
{% else %}
{{ include('guest.html.twig') }}
{% endif %}
リソースクラスはリソース状態だけに関心を持ち、テンプレートだけがリソース表現に関心を持ちます。このような設計原則を関心の分離(SoC)といいます。
エラーページ
var/templates/error.html.twig
を編集します。エラーページには以下の値がアサインされています:
変数 | 意味 | キー |
---|---|---|
status | HTTP ステータス | code, message |
e | 例外 | code, message, class |
logref | ログID | n/a |
例:
{% extends 'layout/base.html.twig' %}
{% block title %}{{ status.code }} {{ status.message }}{% endblock %}
{% block content %}
<h1>{{ status.code }} {{ status.message }}</h1>
{% if status.code == 404 %}
<p>The requested URL was not found on this server.</p>
{% else %}
<p>The server is temporarily unable to service your request.</p>
<p>reference number: {{ logref }}</p>
{% endif %}
{% endblock %}
リソースのアサイン
リソースクラスのプロパティを参照するにはリソース全体がアサインされる_ro
を参照します。
例)Todos.php
:
class Todos extends ResourceObject
{
public $code = 200;
public $text = [
'name' => 'BEAR'
];
public $body = [
['title' => 'run']
];
}
Todos.html.twig
:
{{ _ro.code }} {# 出力: 200 #}
{{ _ro.text.name }} {# 出力: 'BEAR' #}
{% for todo in _ro.body %}
{{ todo.title }} {# 出力: 'run' #}
{% endfor %}
ビューの階層構造
リソースクラス単位でビューを持つことができます。構造を良く表し、キャッシュもリソース単位で行われるので効率的です。
例)app://self/todos
を読み込むpage://self/index
:
app://self/todos
class Todos extends ResourceObject
{
use AuraSqlInject;
use QueryLocatorInject;
public function onGet(): static
{
$this->body = $this->pdo->fetchAll($this->query['todos_list']);
return $this;
}
}
{% for todo in _ro.body %}
{{ todo.title }}
{% endfor %}
page://self/index
class Index extends ResourceObject
{
/**
* @Embed(rel="todos", src="app://self/todos")
*/
public function onGet(): static
{
return $this;
}
}
{% extends 'layout/base.html.twig' %}
{% block content %}
{{ todos|raw }}
{% endblock %}
拡張
TwigをaddExtension()
メソッドで拡張する場合には、拡張を行うTwigのProviderクラスを用意しTwig_Environment
クラスにProvider
束縛します:
use Ray\Di\Di\Named;
use Ray\Di\ProviderInterface;
class MyTwigProvider implements ProviderInterface
{
private $twig;
/**
* @Named("original")
*/
public function __construct(\Twig_Environment $twig)
{
// $twig は元の \Twig_Environment インスタンス
$this->twig = $twig;
}
public function get()
{
// Twigの拡張
$this->twig->addExtension(new MyTwigExtension());
return $this->twig;
}
}
class HtmlModule extends AbstractModule
{
protected function configure()
{
$this->install(new TwigModule);
$this->bind(\Twig_Environment::class)
->toProvider(MyTwigProvider::class)
->in(Scope::SINGLETON);
}
}
モバイル対応
モバイルサイト専用のテンプレートを使用するためにはMobileTwigModule
を加えてインストールします:
class HtmlModule extends AbstractModule
{
protected function configure()
{
$this->install(new TwigModule);
$this->install(new MobileTwigModule);
}
}
index.html.twig
の代わりにIndex.mobile.twig
が存在すれば優先して使用されます。変更の必要なテンプレートだけを用意することができます。
カスタム設定
コンテキストに応じてオプション等を設定したり、テンプレートのパスを追加する場合は@TwigPaths
と@TwigOptions
に設定値を束縛します。
注)キャッシュを常にvar/tmp
フォルダに生成するので、特にプロダクション用の設定などは必要ありません。
namespace MyVendor\MyPackage\Module;
use BEAR\Package\AbstractAppModule;
use Madapaja\TwigModule\Annotation\TwigDebug;
use Madapaja\TwigModule\Annotation\TwigOptions;
use Madapaja\TwigModule\Annotation\TwigPaths;
use Madapaja\TwigModule\TwigModule;
use Ray\Di\AbstractModule;
class AppModule extends AbstractAppModule
{
protected function configure()
{
$this->install(new TwigModule);
// テンプレートパスの指定
$appDir = $this->appMeta->appDir;
$paths = [
$appDir . '/src/Resource',
$appDir . '/var/templates'
];
$this->bind()
->annotatedWith(TwigPaths::class)
->toInstance($paths);
// オプション
// @see http://twig.sensiolabs.org/doc/api.html#environment-options
$options = [
'debug' => false,
'cache' => $appDir . '/tmp'
];
$this->bind()
->annotatedWith(TwigOptions::class)
->toInstance($options);
// debugオプションのみを指定する場合
$this->bind()
->annotatedWith(TwigDebug::class)
->toInstance(true);
}
}