統一されたページレイアウトのサイトをZend Frameworkで作るのにZend_Layoutをよく使う。最もシンプルなレイアウトスクリプトを書くとこんな感じになる。
<?php echo $this->doctype() ?> <html> <head> <?php echo $this->headMeta() ?> <?php echo $this->headTitle() ?> <?php echo $this->headLink() ?> <?php echo $this->headStyle() ?> <?php echo $this->headScript() ?> </head> <body> <?php echo $this->layout()->content ?> </body> </html>
11行目では呼び出されたアクションコントローラに対応するビュースクリプトがレンダリングされる。例えばテキストフィールドのフォームを作り、ボタンをクリックしたら入力した文字列をダイアログに表示させるとしよう。コントローラスクリプトは以下のようにする。
// controllers/IndexController.php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->id = "msg";
}
}
ビュースクリプト側では変数idが参照できるようになる。
// views/scritps/index/index.phtml
<script type="text/javascript">
$(function() {
$('form').submit(function() {
alert($('#<?php echo $this->id ?>').value());
return false;
});
});
</script>
<form>
<input type="text" id="<?php echo $this->id ?>" />
<input type="submit" />
</form>
期待通りの動作をするので悪くはないが、できることならばjQueryを実装する<script>タグは、<body>タグ内ではなく<head>タグに納めたいところ。jQueryの実装を別のjsファイルに書く方法もあるが、アクションコントローラごとにjsファイルを作るのもバカバカしいし、jQuery部分にPHPコードを埋め込めるメリットがなくなってしまう。上記はあまり良い例ではないかもしれないが、HTMLとJavaScriptで共通する変数を扱いたいケースは少なからずある。
レイアウトスクリプトの修正
IndexControllerのindexAction()メソッドに対応するビュースクリプトを例に挙げると、<body>タグ内はindex.phtmlにコーディングして、<head>タグ内に収めたいJavaScriptはindex.js、CSSはindex.cssにコーディングできるようにしたいわけ。
まず、レイアウトスクリプトを以下の用に書き換える。
<?php echo $this->doctype() ?> <html> <head> <?php echo $this->headMeta() ?> <?php echo $this->headTitle() ?> <?php echo $this->headLink() ?> <?php echo $this->headStyle() ?> <?php echo $this->headScript() ?> <?php $javascript = $this->layout()->javascript; if(!empty($javascript)) : ?> <script type="text/javascript"> <?php echo $javascript ?> </script> <?php endif; ?> <?php $stylesheet = $this->layout()->stylesheet; if(!empty($stylesheet)) : ?> <style type="text/css"> <?php echo $stylesheet ?> </style> <?php endif; ?> </head> <body> <?php echo $this->layout()->content ?> </body> </html>
今まではindex.phtml内にJavaScriptやCSSを埋め込むために、いちいち<script>タグや<style>タグなどを書く必要があったが、この部分もレイアウトスクリプトに含めてしまえば、若干ながらコーディング量も減らすことができる。
アクションヘルパーの実装
上記のレイアウトスクリプトでは、Zend_LayoutオブジェクトのjavascriptプレイスホルダにJavaScript部分が、stylesheetプレイスホルダにCSS部分が、それぞれ格納されていることとしてる。これを実現するためのアクションヘルパーを実装していこう。
JavaScriptでもCSSでも、「Zend_Layoutの特定のプレイスホルダにレンダリングしたビュースクリプトの内容を格納する」という部分は共通しているので、まずは基底クラスとなるPartialViewアクションヘルパーを作る。
// controllers/helpers/PartialView.php
class My_Action_Helper_PartialView extends Zend_Controller_Action_Helper_Abstract
{
public function render($name, $suffix, $action=null)
{
$renderer = clone Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$renderer->renderBySpec($action, array('suffix' => $suffix), $name);
}
public function direct($name, $suffix, $action=null)
{
$this->render($name, $suffix, $action);
}
}
render()メソッドの$nameはレンダリング結果を格納するZend_Layoutのコンテンツキーの名前、$suffixは対応するビュースクリプトの拡張子、$actionは呼び出し元のアクションメソッド名となる。7行目では、ヘルパーブローカから、処理中のコンテキストで使われているViewRendererアクションヘルパーを複製している。なぜヘルパーブローカから取得したViewRendererを直接使用しないかというと、後続のrenderBySpec()メソッドの呼び出しによって、処理中のコンテキストで使われているViewRendererに影響を及ぼしてしまうから。なお、$actionを明示的に指定しなかった場合、ViewRendererは処理中のコンテキストから対応するアクションメソッドを補完してくれる。
direct()メソッドはアクションヘルパーのお決まりのパターン。render()メソッドのエイリアスになっている。
続いて、JavaScriptをレンダリングするためのアクションヘルパーを作る。
// controllers/helpers/JsView.php
require_once 'PartialView.php';
class My_Action_Helper_JsView extends My_Action_Helper_PartialView
{
public function direct($action=null)
{
parent::render('javascript', 'js', $action);
}
}
JsViewヘルパーでは、direct()メソッドだけをオーバーライドしている。PartialViewヘルパーのrender()メソッドを呼び出し、ビュースクリプト<action>.jsの内容をレンダリングした結果を、Zend_Layoutのjavascriptプレイスホルダに格納する。
CSSをレンダリングするアクションヘルパーも、同じように書くことができる。
// controllers/helpers/CssView.php
require_once 'PartialView.php';
class My_Action_Helper_CssView extends My_Action_Helper_PartialView
{
public function direct($action=null)
{
parent::render('stylesheet', 'css', $action);
}
}
JsViewアクションヘルパーの使い方
冒頭で例に挙げたIndexControllerのindexAction()メソッドで、JsViewアクションヘルパーを使ってみよう。
// controllers/IndexController.php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->id = "msg";
$this->_helper->jsView();
}
}
これだけ。JavaScriptを実装したいアクションメソッドで$this->_helper->jsView()を呼び出すだけ。CSSも同様に$this->_helper->cssView()を呼び出すだけ。
あとは、対応するビュースクリプトを作ればいい。
// views/scritps/index/index.js
$(function() {
$('form').submit(function() {
alert($('#<?php echo $this->id ?>').value());
return false;
});
});
ビュースクリプトだから、当然PHPのコードを埋め込むこともできるし、$thisを経由してビュー変数にアクセスすることもできる。エディタによっては、拡張子に基づいて予約語などをハイライト表示してくれるので、言語別にファイルを分けられるのは重宝する。
index.phtmlもJavaScriptやCSSのコードが消えてスッキリ。
// views/scritps/index/index.phtml <form> <input type="text" id="<?php echo $this->id ?>" /> <input type="submit" /> </form>