Yii で Twig を利用するにあたって、はじめは twig-view-renderer を使っていたのですが、yiiext / twig-renderer のほうがバージョンが新しいこともあって、こちらを使うことにしました。
DL 後 ETwigViewRenderer.php を protected/extensions 下に設置。次に fabpot / Twig から最新の Twig を DL して、lib 下の Twig ディレクトリをまるごと protected/vendors 下に移動。
今回使ったそれぞれのバージョンは以下になります。
- Twig view renderer - v1.1.2
- Twig - v1.6.0
あとは protected/config/main.php で諸々の設定をしてあげます。Twig view renderer の README や ETwigViewRenderer.php の中身を見ると設定の例が載っています。
とりあえず Twig 自体触りたてなのと、Yii で Twig を使った情報がほとんどなかったので、わからない箇所が多々ありましたが、デフォルトの layouts/column1.php, column2.php, main.php と GiiのCrud Generator で生成したものを Twig に変換したものが以下になります。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends 'views/layouts/main.twig' %} | |
{% block container %} | |
<div class="container"> | |
<div id="content"> | |
{% block content %} | |
{{ content }} | |
{% endblock content %} | |
</div><!-- /#content --> | |
</div><!-- /.container --> | |
{% endblock container %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends 'views/layouts/main.twig' %} | |
{% block container %} | |
<div class="container"> | |
<div class="span-19"> | |
<div id="content"> | |
{% block content %} | |
{{ content }} | |
{% endblock content %} | |
</div><!-- ./content --> | |
</div><!-- /.span-19 --> | |
<div class="span-5 last"> | |
<div id="sidebar"> | |
{% do this.beginWidget('zii.widgets.CPortlet', { | |
'title': 'Operations', | |
}) %} | |
{% do this.widget('zii.widgets.CMenu', { | |
'items': this.menu, | |
'htmlOptions': {'class': 'operations'}, | |
}) %} | |
{% do this.endWidget %} | |
</div><!-- /.sidebar --> | |
</div><!-- /.span-5 last --> | |
</div><!-- /.container --> | |
{% endblock container %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
<meta name="language" content="ja" /> | |
<!-- blueprint CSS framework --> | |
<link rel="stylesheet" type="text/css" href="{{ App.baseUrl }}/css/screen.css" media="screen, projection" /> | |
<link rel="stylesheet" type="text/css" href="{{ App.baseUrl }}/css/print.css" media="print" /> | |
<!--[if lt IE 8]> | |
<link rel="stylesheet" type="text/css" href="{{ App.baseUrl }}/css/ie.css" media="screen, projection" /> | |
<![endif]--> | |
<link rel="stylesheet" type="text/css" href="{{ App.baseUrl }}/css/main.css" /> | |
<link rel="stylesheet" type="text/css" href="{{ App.baseUrl }}/css/form.css" /> | |
<title>{{ this.pageTitle|e }}</title> | |
</head> | |
<body> | |
<div class="container" id="page"> | |
<div id="header"> | |
<div id="logo">{{ App.name|e }}</div> | |
</div><!-- /#header --> | |
<div id="mainmenu"> | |
{% do this.widget('zii.widgets.CMenu', { | |
'items': [ | |
{'label': 'Home', 'url': ['/hoge/index']}, | |
{'label': 'About', 'url': ['/site/about']}, | |
{'label': 'Contact', 'url': ['/site/contact']}, | |
{'label': 'Login', 'url': ['/site/login'], 'visible': App.user.isGuest}, | |
{'label': 'Logout ('~App.user.name~')', 'url': ['/site/logout'], 'visible': not App.user.isGuest}, | |
{'label': 'Gii', 'url': ['/gii']}, | |
], | |
}) %} | |
</div><!-- /#mainmenu --> | |
{% if this.breadcrumbs %} | |
{% do this.widget('zii.widgets.CBreadcrumbs', { | |
'links': this.breadcrumbs, | |
}) %} | |
{% endif %} | |
{% block container %} | |
{% endblock container %} | |
<div id="footer"> | |
Copyright © {{ now|date('Y') }} by My Company.<br/> | |
All Rights Reserved.<br/> | |
{{ Yii.powered }} | |
</div><!-- /#footer --> | |
</div><!-- /.container #page --> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% do this.setBreadcrumbs(['Hoges']) %} | |
{% do this.setMenu([ | |
{'label': 'Create Hoge', 'url': ['create']}, | |
{'label': 'Manage Hoge', 'url': ['admin']}, | |
]) %} | |
<h1>Hoges</h1> | |
{% do this.widget('zii.widgets.CListView', { | |
'dataProvider': dataProvider, | |
'itemView': '_view', | |
}) %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="view"> | |
<b>{{ data.getAttributeLabel('id')|e }}</b> | |
{{ C.Html.link(data.id|e, {0: 'view', 'id': data.id}) }} | |
<br /> | |
<b>{{ data.getAttributeLabel('userid')|e }}</b> | |
{{ data.userid|e }} | |
<br /> | |
<b>{{ data.getAttributeLabel('fuga')|e }}</b> | |
{{ data.fuga|e }} | |
<br /> | |
<b>{{ data.getAttributeLabel('piyo')|e }}</b> | |
{{ data.piyo|e }} | |
<br /> | |
</div><!-- /.view --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% do this.setBreadCrumbs({ | |
'Hoges': ['index'], | |
0: 'Manage', | |
}) %} | |
{% do this.setMenu([ | |
{'label': 'List Hoge', 'url': ['index']}, | |
{'label': 'Create Hoge', 'url': ['create']}, | |
]) %} | |
{{ void(App.clientScript.registerScript('search', " | |
$('.search-button').click(function(){ | |
$('.search-form').toggle(); | |
return false; | |
}); | |
$('.search-form form').submit(function(){ | |
$.fn.yiiGridView.update('hoge-grid', { | |
data: $(this).serialize() | |
}); | |
return false; | |
}); | |
")) }} | |
<h1>Manage Hoges</h1> | |
<p> | |
You may optionally enter a comparison operator (<b><</b>, <b><=</b>, <b>></b>, <b>>=</b>, <b><></b> | |
or <b>=</b>) at the beginning of each of your search values to specify how the comparison should be done. | |
</p> | |
{{ C.Html.link('Advanced Search', '#', {'class': 'search-button'}) }} | |
<div class="search-form" style="display:none"> | |
{% include 'views/hoge/_search.twig' with {'model': model} %} | |
</div><!-- /.search-form --> | |
{% do this.widget('zii.widgets.grid.CGridView', { | |
'id': 'hoge-grid', | |
'dataProvider': model.search, | |
'filter': model, | |
'columns': [ | |
'id', 'userid', 'fuga', 'piyo', | |
{'class': 'CButtonColumn'}, | |
], | |
}) %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% do this.setBreadcrumbs({ | |
'Hoges': ['index'], | |
0: model.id, | |
}) %} | |
{% do this.setMenu([ | |
{'label': 'List Hoge', 'url': ['index']}, | |
{'label': 'Create Hoge', 'url': ['create']}, | |
{'label': 'Update Hoge', 'url': {0: 'update', 'id': model.id}}, | |
{'label': 'Delete Hoge', 'url': '#', 'linkOptions': {'submit': {0: 'delete', 'id': model.id}, 'confirm': 'Are you sure you want to delete this item?'}}, | |
{'label': 'Manage Hoge', 'url': ['admin']}, | |
]) %} | |
<h1>View Hoge #{{ model.id }}</h1> | |
{% do this.widget('zii.widgets.CDetailView', { | |
'data': model, | |
'attributes': [ | |
'id', 'userid', 'fuga', 'piyo', 'is_deleted', | |
] | |
}) %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="form"> | |
{% set form = this.beginWidget('CActiveForm', { | |
'id': 'hoge-form', | |
'enableAjaxValidation': false, | |
}) %} | |
<p class="note">Fields with <span class="required">*</span> are required.</p> | |
{{ form.errorSummary(model) }} | |
<div class="row"> | |
{{ form.labelEx(model, 'userid') }} | |
{{ form.textField(model, 'userid') }} | |
{{ form.error(model, 'userid') }} | |
</div><!-- /.row --> | |
<div class="row"> | |
{{ form.labelEx(model, 'fuga') }} | |
{{ form.textField(model, 'fuga', {'size': 60, 'maxlength': 64}) }} | |
{{ form.error(model, 'fuga') }} | |
</div><!-- /.row --> | |
<div class="row"> | |
{{ form.labelEx(model, 'piyo') }} | |
{{ form.textField(model, 'piyo', {'size': 60, 'maxlength': 64}) }} | |
{{ form.error(model, 'piyo') }} | |
</div><!-- /.row --> | |
<div class="row buttons"> | |
{{ C.Html.submitButton(model.isNewRecord ? 'Create' : 'Save') }} | |
</div><!-- /.row buttons --> | |
{% do this.endWidget %} | |
</div><!-- form --> |
わからなかった箇所
- オプションで 'autoescape' => true とするとすべての HTML タグが変換されてしまう ( ので、手動エスケープで対処 )
ビューでオブジェクトのプロパティを設定できない (のでコントローラに書いている )「CActiveForm ウィジェットの値を $form に代入する」ということがビューではできない (ので CHtml::form() で代用)CHtml::link() を使うと URLに 0%5Bid%5D みたいなものが付いてしまう (ので$this->createUrl で代用)layouts/column2.twig の CPortlet ウィジェットがどうしてもエラーになる (ので省略)Yii::app() は App. で呼び出せるが Yii::getVersion() の呼び出し方がわからない
便利そうだなと思う箇所
- オプションで CHtml::link()を link() で呼び出せるように設定できる
とりあえず、現段階では Yii の機能も Twig の機能も殺してしまっている感じなので、もう少し勉強したあとで、また再度試してみたいと思います。
追加補足
CHtml::link() で 0%5Bid%5D が付いてしまう件は記述間違いでした。正しくは以下。
echo C.Html.link(data.id|e, {0: 'id', 'id': data.id}); ( 0 を付ける形になります)
Yii::getVersion() などの呼び出し方はコメントで答えを頂きました。参考にしてください。
CActiveForm ウィジェットの件は こちら こちらで解決できました。コメント側で表現されているやり方のほうが良さそうに見えました。
layouts/column2.twig の CPortlet ウィジェットの件も、同じような形で表現して解決できました。column2.html のコードを更新しましたので、参考にしてみてください。
ビューでオブジェクトのプロパティを設定できない件は Controller.php で public setBreadcrumbs($value) { $this->breadcrumbs = $value; } とすることで解決しました (menuプロパティも同様) 。
ETwigViewRenderer の forum に書き込まれていたのでこちらに記事が書かれているかもと思いやってきました。
返信削除protected/config/main.php の globals に
'viewRenderer'=>array(
'class'=>'ext.etwigviewrenderer.ETwigViewRenderer',
'globals' => array(
'Yii' => 'Yii',
),
),
Yii を加えて {{ Yii.getVersion }} で呼び出せましたよ。
なるほど!ありがとうございます!
削除globalsとfunctionsはすごく使えそうだなと思いました
ただ、多く設定した場合のパフォーマンスが気になりますが
もひとつ。
返信削除{% set form = this.createWidget() %} すればwidgetをあきらめなくてもいいかも知れません。