Zend Framework の view エンジンとして Smarty を使用するやり方は、ネット上にいくらでも紹介されているし、このブログでも先日ネタにした (Zend Framework と Smarty)。
しかし、Bootstrap で Smarty を組み込むと、エラーなどで例外が発生した場合にも Smarty を使用してレンダリングすることになる。
標準の (Zend_Tool で作成した場合だったかな?) ErrorController と view スクリプト error.phtml の組み合わせは、view エンジンが Smarty に置き換えてしまっているので、そのままでは動作しない。そこで、例えば、view スクリプト error.phtml を Smarty 用に書き換えた error.tpl のようなものを自分で用意してやらなければならない。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Zend Framework Default Application</title>
</head>
<body>
<h1>An error occurred</h1>
<h2><?php echo $this->message ?></h2>
<?php if ('development' == APPLICATION_ENV): ?>
<h3>Exception information:</h3>
<p>
<b>Message:</b> <?php echo $this->exception->getMessage() ?>
</p>
<h3>Stack trace:</h3>
<pre><?php echo $this->exception->getTraceAsString() ?>
</pre>
<h3>Request Parameters:</h3>
<pre><?php echo var_export($this->request->getParams(), true) ?>
</pre>
<?php endif ?>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Zend Framework Default Application</title>
</head>
<body>
<h1>An error occurred</h1>
<h2>{$message}</h2>
<!-- ?php if ('development' == APPLICATION_ENV): ? -->
<h3>Exception information:</h3>
<p>
<b>Message:</b> {$exception->getMessage()}
</p>
<h3>Stack trace:</h3>
<pre>{$exception->getTraceAsString()}
</pre>
<h3>Request Parameters:</h3>
<pre>
{foreach from=$request->getParams() key=k item=v}
'{$k}' => '{$v}'
{/foreach}
</pre>
<!-- ?php endif ? -->
<div>smarty</div>
</body>
</html>
だが、エラーをハンドリングする場合は Smarty ではなく、Zend_View の方でレンダリングして欲しいと思うケースだってあるだろう。
ここでは、ErrorController の中で View エンジンを Zend_View に設定しなおす方法について紹介する。
結論を先に言うと、下のようなコードを追加すれば良い。
<?php
class ErrorController extends Zend_Controller_Action
{
public function errorAction()
{
if (null !== $this->view and get_class($this->view) != 'Zend_View') {
$view = new Zend_View();
$this->view = null;
$viewRenderer = $this->getHelper('ViewRenderer');
$viewRenderer->setView($view)
->setViewSuffix('phtml')
->initView();
}
$errors = $this->_getParam('error_handler');
switch ($errors->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
// 404 error -- controller or action not found
$this->getResponse()->setHttpResponseCode(404);
$this->view->message = 'Page not found';
break;
default:
// application error
$this->getResponse()->setHttpResponseCode(500);
$this->view->message = 'Application error';
break;
}
$this->view->exception = $errors->exception;
$this->view->request = $errors->request;
}
}
errorAction() が呼び出された時点で、$this->view は Zend_View_Smarty クラスのオブジェクトが設定されている。これを単純に Zend_View に置き換えても動作しない。$this->getHelper(‘ViewRenderer’) ($this->_helper->viewRenderer と結果は同じ) で取得できる ViewRenderer に View をセットし、かつ、View の初期設定を行わなければならない。
初期設定のための情報は ViewRenderer 内にあって、initView() ($viewRenderer->initView()) を実行すると ViewRenderer に設定された View に対する初期化が行われる。また、アクションコントローラの view ($this->view) が null だと、下のようにアクションコントローラの view に設定してくれる。
// Register view with action controller (unless already registered)
if ((null !== $this->_actionController) && (null === $this->_actionController->view)) {
$this->_actionController->view = $this->view;
$this->_actionController->viewSuffix = $this->_viewSuffix;
}
今回調べていて感じたのだが、Zend Framework は複数の view エンジンを動的に切り替えて使うようには設計されていないようだ。
上書きする形で置き換えることはできるが、一旦設定されてしまったあとの内部の状態をキャンセルしたり、元に戻したりするのに洗練された方法が見当たらない。
なので、今回のように、Bootstrap で設定したものをアクションコントローラ内で更に設定し直すよりも、Bootstrap では変更しないでアクションの最後のほうで設定する方針としたほうが良いかもしれない。