动作助手可以向任何Zend_Controller_Action的衍生动作控制器中,即时的加入功能(runtime and/or on-demand functionality),以使得增加公共的动作控制器功能时,尽量减少衍生动作控制器类的必要。
动作助手有多种使用方式。动作助手使用了一套经纪系统(brokerage system),与Zend_View_Helpers中使用的,也就是Zend_Controller_Plugin的经纪系统类似。动作助手在需要调用时加载,可以在请求的时候(bootstrap)或者动作控制器创建的时候(init())实例化。要充分了解这些细节,请阅读下面的章节。
根据需求以及助手的功能,可有几种不同的初始化方式。
助手经纪人(broker)存储在Zend_Controller_Action的 $_helper成员中,可以使用经纪人来获取或者调用助手。以下是操作方法:
显式的调用getHelper()方法。简单的传入助手名字,就可以返回助手对象。
<?php
$flashMessenger = $this->_helper->getHelper('FlashMessenger');
$flashMessenger->addMessage('We did something in the last request');
使用助手经纪人的__get()方法,就像获取经纪人的成员属性一样获取助手。
<?php
$flashMessenger = $this->_helper->FlashMessenger;
$flashMessenger->addMessage('We did something in the last request');
最后,大部分动作助手实现了direct()方法,它将调用助手的一个特定的,默认的方法。以FlashMessenger为例,它调用了方法addMessage():
<?php
$this->_helper->FlashMessenger('We did something in the last request');
![]() |
注意 |
|---|---|
上面的例子在功能上是等价的。 |
也可以显式的实例化助手。如果你要在动作控制器之外使用助手,或者给助手经纪人传入一个助手供所有的动作使用,你可能希望直接这么做。实例化助手和实例化其他PHP类的方法一样。
Zend_Controller_Action_HelperBroker处理注册助手对象和助手路径,即时的获取助手等细节。
使用addHelper来注册助手:
<?php
Zend_Controller_Action_HelperBroker::addHelper($helper);
实例化助手并传入经纪人有点耗费时间和资源,不过addPrefix()和addPath()两个方法能够很容易的自动完成这些工作:
addPrefix()方法带有一个类前缀参数,用来加入自定义助手类的路径。它假定前缀遵循Zend Framework的类命名惯例。
<?php
// Add helpers prefixed with My_Action_Helpers in My/Action/Helpers/
Zend_Controller_Action_HelperBroker::addPrefix('My_Action_Helpers');
addPath()方法第一个参数为一个目录,第二个为类前缀(默认为'Zend_Controller_Action_Helper')。用来将自己的类前缀映射到指定的目录。
<?php
// Add helpers prefixed with Helper in Plugins/Helpers/
Zend_Controller_Action_HelperBroker::addPath('./Plugins/Helpers', 'Helper');
这些方法是静态的,因而可以根据需要在控制器链中的任何位置调用动态的加载助手。
使用hasHelper($name)方法来判定助手经纪人中是否存在某助手,$name是助手的短名称(去掉前缀的):
<?php
// Check if 'redirector' helper is registered with the broker:
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
echo 'Redirector helper registered';
}
最后,使用removeHelper($name)来删除助手经纪人中的某个助手,$name是助手的短名称。
<?php
// Conditionally remove the 'redirector' helper from the broker:
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
Zend_Controller_Action_HelperBroker::removeHelper('redirector')
}
Zend Framework默认包含三个动作助手:FlashMessenger用来处理Flash Messenger会话; Redirector提供另一种实现方式,帮助程序重定向到内部或者外部页面;ViewRenderer自动的完成在控制器内建立视图对象并渲染视图的过程。
FlashMessenger助手允许你传递用户可能需要在下个请求看到的消息。为实现它,FlashMessenger使用Zend_Session_Namespace来存储消息以备将来或下个请求来读取。如果你计划使用Zend_Session或者Zend_Session_Namespace,在引导文件里用Zend_Session::start()初始化,的确是个好主意。(参见Zend_Session文档有更多的关于它的用法的细节。)
下面的例子展示flash messenger最基本的用法。当动作/some/my 被调用,它添加flash message "Record Saved!",随后的对动作/some/my-next-request的请求将读取它(并也这样删除它)。
<?php
class SomeController extends Zend_Controller_Action
{
/**
* FlashMessenger
*
* @var Zend_Controller_Action_Helper_FlashMessenger
*/
protected $_flashMessenger = null;
public function init()
{
$this->_flashMessenger = $this->_helper->getHelper('FlashMessenger');
$this->initView();
}
public function myAction()
{
/**
* default method of getting Zend_Controller_Action_Helper_FlashMessenger
* instance on-demand
*/
$this->_flashMessenger->addMessage('Record Saved!');
}
public function myNextRequestAction()
{
$this->view->messages = $this->_flashMessenger->getMessages();
$this->render();
}
}
转向器(Redirector)助手允许使用一个转向器对象帮助程序重定向到新的URL。与_redirect()方法相比,它具有多项优势。例如能够在转向器对象中预先配置整个站点的行为,或者使用与Zend_Controller_Action::_forward()相似的goto($action, $controller, $module, $params)接口。
转向器拥有影响重定向行为的大量方法:
setCode() 设置重定向过程中使用的HTTP响应码。
setExit() 在重定向后强制执行exit()方法。默认已设定。
setGoto()设置默认的URL,当没有提供参数给goto()方法时转向该URL。可以使用类似Zend_Controller_Action::_forward()的API:setgoto($action, $controller = null, $module = null, array $params = array());
setGotoRoute()设置基于一个注册路由器的URL。通过传入一个键/值数组和一个路由器名,它将根据路由器的类型和定义来组织URL。
setGotoUrl()设置默认的URL,当没有参数传入gotoUrl(),将使用该URL。接受单个URL字符串。
setPrependBase()在setGotoUrl()、gotoUrl()或者gotoUrlAndExit()指定的URL前面,加入请求对象的基地址(base URL)。
setUseAbsoluteUri()强制转向器在重定向时使用绝对的URI。当该选项设定后,将使用$_SERVER['HTTP_HOST']、 $_SERVER['SERVER_PORT']和 $_SERVER['HTTPS']以及重定向方法指定的URL,来形成一个完整的URI。该选项目前默认关闭,将来的版本可能会默认开启。
此外,转向器中还有大量方法来执行实际的重定向。
goto()使用setGoto()(类似_forward()的API)来构建URL并执行重定向。
gotoRoute()使用setGotoRoute()(路由组装route-assembly)来构建URL并执行重定向。
gotoUrl()使用setGotoUrl() URL字符串)来构造URL并执行重定向。
最后,你可以在任何时刻使用getRedirectUrl()确定当前的重定向URL。
例 7.2. 设定选项
这个例子改变了几个选项,包括设定重定向时使用的HTTP状态码为303,重定向时不默认退出,以及定义了默认的URL供重定向使用。
<?php
class SomeController extends Zend_Controller_Action
{
/**
* Redirector - defined for code completion
*
* @var Zend_Controller_Action_Helper_Redirector
*/
protected $_redirector = null;
public function init()
{
$this->_redirector = $this->_helper->getHelper('Redirector');
// Set the default options for the redirector
// Since the object is registered in the helper broker, these become
// relevant for all actions from this point forward
$this->_redirector->setCode('303')
->setExit(false)
->setGoto("this-action", "some-controller");
}
public function myAction()
{
/* do some stuff */
// Redirect to a previously registered URL, and force an exit to occur
// when done:
$this->_redirector->redirectAndExit();
return; // never reached
}
}
例 7.3. 使用默认设定
这个例子假定使用默认设定,也就意味着任何重定向将导致立即退出。
<?php
// ALTERNATIVE EXAMPLE
class AlternativeController extends Zend_Controller_Action
{
/**
* Redirector - defined for code completion
*
* @var Zend_Controller_Action_Helper_Redirector
*/
protected $_redirector = null;
public function init()
{
$this->_redirector = $this->_helper->getHelper('Redirector');
}
public function myAction()
{
/* do some stuff */
$this->_redirector->gotoUrl('/my-controller/my-action/param1/test/param2/test2');
return; // never reached since default is to goto and exit
}
}
例 7.4. 使用goto()的_forward()API
goto()'s API 模拟了Zend_Controller_Action::_forward()。主要的不同在于它通过传入的参数构造URL,使用默认路由器的默认格式:module/:controller/:action/*。然后重定向而不是继续动作链循环。
<?php
class ForwardController extends Zend_Controller_Action
{
/**
* Redirector - defined for code completion
*
* @var Zend_Controller_Action_Helper_Redirector
*/
protected $_redirector = null;
public function init()
{
$this->_redirector = $this->_helper->getHelper('Redirector');
}
public function myAction()
{
/* do some stuff */
// Redirect to 'my-action' of 'my-controller' in the current module,
// using the params param1 => test and param2 => test2
$this->_redirector->goto('my-action', 'my-controller', null, array('param1' => 'test', 'param2' => 'test2'));
}
}
例 7.5. 通过gotoRout()使用路由组装(route assembly)
下面的例子使用了路由器的assemble()方法,基于传入参数的关联数组来创建URL。假定下面的路由已经注册:
<?php
$route = new Zend_Controller_Router_Route(
'blog/:year/:month/:day/:id',
array('controller' => 'archive', 'module' => 'blog', 'action' => 'view')
);
$router->addRoute('blogArchive', $route);
给定一个数组,其中年份为2006,月份为4,日期为24,id为42,据此可以组装URL/blog/2006/4/24/42。
<?php
class BlogAdminController extends Zend_Controller_Action
{
/**
* Redirector - defined for code completion
*
* @var Zend_Controller_Action_Helper_Redirector
*/
protected $_redirector = null;
public function init()
{
$this->_redirector = $this->_helper->getHelper('Redirector');
}
public function returnAction()
{
/* do some stuff */
// Redirect to blog archive. Builds the following URL:
// /blog/2006/4/24/42
$this->_redirector->gotoRoute(
array('year' => 2006, 'month' => 4, 'day' => 24, 'id' => 42),
'blogArchive'
);
}
}
视图渲染(ViewRenderer)助手为实现下列目标设计:
不需要在控制器内创建视图对象实例;视图对象将在控制器内自动注册。
根据当前的模块自动地设置视图脚本、助手、过滤器路径。指派当前的模块名为助手和过滤器类的类名前缀。
为所有分发的控制器和动作创建全局有效的视图对象。
允许开发人员为所有控制器设置默认的视图渲染选项。
加入无需干预自动渲染试图脚本的功能。
允许开发人员为视图基路径和视图脚本路径创建自己的规范。
![]() |
注意 |
|---|---|
如果手动执行 |
![]() |
注意 |
|---|---|
|
如希望在分发前端控制器前修改
|
大多数使用中,只需要简单的创建 ViewRenderer对象,然后传入到动作助手经纪人。创建实例并注册的最简单方式是使用助手经纪人的getStaticHelper()方法:
<?php
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
动作控制器第一次实例化时,会触发ViewRenderer创建一个视图对象。动作控制器每次实例化都会调用ViewRenderer的init()方法,设定动作控制器的视图属性,并以相对于当前模块的路径为参数调用addScriptPath()方法;调用时带有以当前模块命名的类前缀参数,该参数对为该模块定义的所有助手和过滤器类都有效。(this will be called with a class prefix named after the current module, effectively namespacing all helper and filter classes you define for the module. )
每次执行postDispatch()方法,它将为当前动作执行render()方法。
例如这个类:
<?php
// A controller class, foo module:
class Foo_BarController extends Zend_Controller_Action
{
// Render bar/index.phtml by default; no action required
public function indexAction()
{
}
// Render bar/populate.phtml with variable 'foo' set to 'bar'.
// Since view object defined at preDispatch(), it's already available.
public function populateAction()
{
$this->view->foo = 'bar';
}
}
...
// in one of your view scripts:
<?php $this->foo(); // call Foo_View_Helper_Foo::foo()
ViewRenderer也定义了大量的访问器用来设定和获取视图选项。
setView($view)可以为ViewRenderer设定视图对象。以公共类属性$view获取设定值。
setNeverRender($flag = true)可以全局的启用或禁用自动渲染,也就是对所有控制器都有效。如果设定为true,在所有控制器器内,postDispatch()将不会自动调用render()。getNeverRender()返回当前的设定值。
setNoRender($flag = true) 用来启用或禁用自动渲染,如果设置为true,在当前控制器内,postDispatch()不会调用render()方法。这个设定在preDispatch()每次执行时会被重置。getNoRender()返回当前的设定值。
setNoController($flag = true)通知render()不要再到以控制器命名的子目录中寻找视图脚本。getNoController()返回当前值。
setNeverController($flag = true)与setNoController($flag = true)相似,但是其在全局范围内有效——也就是说,它不会在每次分发动作时重置。getNeverController()返回当前值。
setScriptAction($name)用来指定渲染的视图脚本。$name是脚本的名字去掉后缀(不带控制器子目录,除非noController已开启)。如果没有指定,它将寻找以请求对象中的动作命名的视图脚本。getScriptAction()返回当前值。
setResponseSegment($name)用来指定渲染到响应对象中的哪个命名片段。如果没有指定,渲染到默认片断。getResponseSegment()返回当前值。
initView($path, $prefix, $options可以指定视图的基路径,为助手和过滤器脚本设置类前缀,设定ViewRenderer选项。可以传入以下任意的标志:neverRender,noRender,noController, scriptAction,和responseSegment。
setRender($action = null, $name = null, $noController = false)可以一次设定scriptAction、responseSegment和noController。 direct()是它的别名,使得控制器中可以方便的调用。
// Render 'foo' instead of current action script
$this->_helper->viewRenderer('foo');
// render form.phtml to the 'html' response segment, without using a
// controller view script subdirectory:
$this->_helper->viewRenderer('form', 'html', true);
![]() |
注意 |
|---|---|
|
构造函数允许可选的传入参数视图对象和ViewRenderer选项,接受与initView()一样的标志(flags):
$view = new Zend_View(array('encoding' => 'UTF-8'));
$options = array('noController' => true, 'neverRender' => true);
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view, $options);
还有几个额外的方法用来定制路径规则,供确定视图基路径来增加视图对象,确定视图脚本路径查找并渲染视图脚本时使用。这些方法每个都带有下面一个或更多的占位符(placehodlers)。
:moduleDir 引用当前模块的基目录(常规的是模块的控制器目录的父目录)。
:module 引用当前的模块名。
:controller 引用当前的控制器名。
:action引用当前的模块名。
:suffix 引用当前的视图脚本后缀(可以通过setViewSuffix()来设置)。
控制器路径规则有关的方法:
setViewBasePathSpec($spec)可以改变确定加入到视图对象的基路径的路径规则。默认规则是:moduleDir/views。任何时候都可以使用getViewBasePathSpec()获取当前的规则。
setViewScriptPathSpec($spec)允许改变确定到达单独的视图脚本路径(去除试图脚本基路径)的路径规则。默认的路径规则是 :controller/:action.:suffix。任何时候都可以通过getViewScriptPathSpec()获取当前规则。
setViewScriptPathNoControllerSpec($spec)允许改变 noController有效时确定到达单独的视图脚本路径(去除试图脚本基路径)的路径规则。默认的规则是:action.:suffix,任何时候都可以通过getViewScriptPathNoControllerSpec()获取当前规则。
ViewRenderer API中的最后一项是关于实际确定视图脚本路径和渲染视图的。包括:
renderScript($script, $name)允许渲染指定路径的脚本,可选的命名的路径片段。(renderScript($script, $name) allows you to render a script with a path you specify, optionally to a named path segment. )使用该方法时,ViewRenderer不会自动的确定脚本名称,而是直接的向视图对象的render()传入$script参数。
![]() |
注意 |
|---|---|
当视图已经被渲染到响应对象,将会设置 |
![]() |
注意 |
|---|---|
默认的, |
getViewScript($action, $vars)基于传入的动作和/或$vars中的变量创建到视图脚本的路径。该数组中的键可以包含所有的路径指定键('moduleDir','module', 'controller', 'action', and 'suffix')。传入的任何变量都会优先使用,否则利用基于当前请求的值。
getViewScript()根据noController标志的设定值使用viewScriptPathSpec或者viewScriptPathNoControllerSpec。
模块、控制器以及动作中的单词定界符将后替换成短线('-')。因此,控制器名称'foo.bar'和动作'baz:bat'按照默认的路径规则将会得到视图脚本路径'foo-bar/baz-bat.phtml'。
![]() |
注意 |
|---|---|
默认的, |
render($action, $name, $noController)首先检查$name或 $noController参数是否传入,如果传入,则在ViewRenderer中设定相应的标志(分别是响应片段和noController)。然后传入$action参数到getViewScript(),最后传入计算的试图脚本路径到renderScript()。
![]() |
注意 |
|---|---|
注意使用render()的边际效应:传入的响应片段名称和noController标志在视图对象中存留。此外渲染结束后noRender会被设置。 |
![]() |
注意 |
|---|---|
默认的, |
renderBySpec($action, $vars, $name)允许传入路径规则变量以确定创建的视图脚本路径。它把$action和$vars传入到getScriptPath(),将脚本路径结果和$name传入到renderScript()。
例 7.6. 基本用法
大多数基础使用中,只需在bootstrap中使用助手经纪人简单的初始化和注册ViewRenderer 助手,然后在动作方法中设置变量。
<?php
// In your bootstrap:
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
...
<?php
// 'foo' module, 'bar' controller:
class Foo_BarController extends Zend_Controller_Action
{
// Render bar/index.phtml by default; no action required
public function indexAction()
{
}
// Render bar/populate.phtml with variable 'foo' set to 'bar'.
// Since view object defined at preDispatch(), it's already available.
public function populateAction()
{
$this->view->foo = 'bar';
}
// Renders nothing as it forwards to another action; the new action
// will perform any rendering
public function bazAction()
{
$this->_forward('index');
}
// Renders nothing as it redirects to another location
public function batAction()
{
$this->_redirect('/index');
}
}
![]() |
命名规则:控制器和动作名中的单词定界符 |
|---|---|
|
如果控制器或者动作名称由几个单词组成,分发器要求在URL中使用特定的路径和单词定界符分隔。
注意到在第二个例子中,模块依然是默认的模块,但由于路径分隔符的存在,控制器的接收到的名字为 |
例 7.7. 禁用自动渲染
对于某些动作和控制器,可能希望关闭自动渲染——例如,如果想发送其他类型的输出(XML,JSON等),或者更简单的不想发送任何东西。有两个选项:关闭所有的自动渲染(setNeverRender()),或者仅仅关闭当前动作的自动渲染(setNoRender())。
<?php
// Baz controller class, bar module:
class Bar_BazController extends Zend_Controller_Action
{
public function fooAction()
{
// Don't auto render this action
$this->_helper->viewRenderer->setNoRender();
}
}
// Bat controller class, bar module:
class Bar_BatController extends Zend_Controller_Action
{
public function preDispatch()
{
// Never auto render this controller's actions
$this->_helper->viewRenderer->setNoRender();
}
}
![]() |
注意 |
|---|---|
大多数情况下,全局的关闭自动渲染( |
例 7.8. 选择另外的视图脚本
有些情况下需要渲染另一个脚本而非以动作命名的脚本。例如,如果你有一个控制器包含增加和编辑两个动作,它们可能都显示相同的'form'视图,尽管拥有不同的值集合(value set)。只需要使用setScriptAction()或者setRender()简单的改变脚本的名称,或者以成员方法的形式调用助手,它将调用setRender()。
<?php
// Bar controller class, foo module:
class Foo_BarController extends Zend_Controller_Action
{
public function addAction()
{
// Render 'bar/form.phtml' instead of 'bar/add.phtml'
$this->_helper->viewRenderer('form');
}
public function editAction()
{
// Render 'bar/form.phtml' instead of 'bar/edit.phtml'
$this->_helper->viewRenderer->setScriptAction('form');
}
public function processAction()
{
// do some validation...
if (!$valid) {
// Render 'bar/form.phtml' instead of 'bar/process.phtml'
$this->_helper->viewRenderer->setRender('form');
return;
}
// otherwise continue processing...
}
}
例 7.9. 修改注册的视图Modifying the registered view
如果需要修改视图对象怎么办——例如改变助手路径或者编码?可以在控制器中修改视图对象设定,或者从ViewRenderer中抓取视图对象;两种方式引用的是同一个对象。
<?php
// Bar controller class, foo module:
class Foo_BarController extends Zend_Controller_Action
{
public function preDispatch()
{
// change view encoding
$this->view->setEncoding('UTF-8');
}
public function bazAction()
{
// Get view object and set escape callback to 'htmlspecialchars'
$view = $this->_helper->viewRenderer->view;
$view->setEscape('htmlspecialchars');
}
}
例 7.10. 修改路径规则
有些情况下,默认的路径规则可能并不适合站点的需要。比如,希望拥有一个单独的模板树供设计人员访问(例如,如果你使用Smarty,这是很典型的情形)。这种情况下,你可能想硬编码视图的基路径规则,为动作视图脚本路径自身创建一套规则。
假定视图的基路径(base path)为'/opt/vendor/templates',希望通过':moduleDir/:controller/:action.:suffix'引用视图脚本;如果设定了noController标志,想在顶级而不是在子目录中渲染(':action.:suffix')。最终希望使用'tpl'作为视图脚本文件的后缀。
<?php
/**
* In your bootstrap:
*/
// Different view implementation
$view = new ZF_Smarty();
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
$viewRenderer->setViewBasePathSpec('/opt/vendor/templates')
->setViewScriptPathSpec(':module/:controller/:action.:suffix')
->setViewScriptPathNoControllerSpec(':action.:suffix')
->setViewSuffix('tpl');
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
例 7.11. 一个动作中渲染多个视图脚本
有时可能需要在一个动作中渲染多个视图脚本。这个非常简单,多次调用render()就行了:
<?php
class SearchController extends Zend_Controller_Action
{
public function resultsAction()
{
// Assume $this->model is the current model
$this->view->results = $this->model->find($this->_getParam('query', '');
// render() by default proxies to the ViewRenderer
// Render first the search form and then the results
$this->render('form');
$this->render('results');
}
public function formAction()
{
// do nothing; ViewRenderer autorenders the view script
}
}
动作助手继承抽象类Zend_Controller_Action_Helper_Abstract,该类提供了助手经纪人要求的基本接口和功能,包含下列方法:
setActionController() 用来设置当前的动作控制器。
init(),该方法在实例化时由助手经纪人触发,可用来触发助手的初始化过程;动作链中多个控制器使用相同的助手时,如要恢复状态时将十分有用。
preDispatch()分发动作之前触发。
postDispatch()分发过程结束时触发——即使preDispatch()插件已经跳过了该动作。清理时大量使用。
getRequest() 获取当前的请求对象。
getResponse() 获取当前的响应对象。
getName() 获取助手名。获取了下划线后面的类名部分,没有下划线则获取类的全名。例如,如果类名为Zend_Controller_Action_Helper_Redirector,他将返回 Redirector,如果类名为FooMessage,将会返回全名。
助手类中还可以包含一个direct()方法,如果定义了该方法,就可以将助手视作助手经纪人的一个方法,以便简单的、一次性的使用助手。例如redirector 定义direct() 作为goto()的别名,就可以这样使用:
<?php
// Redirect to /blog/view/item/id/42
$this->_helper->redirector('item', 'view', 'blog', array('id' => 42));
在内部,助手经纪人的__call()方法先寻找名叫redirector的助手,然后检查该助手的direct方法是否定义,然后使用所提供的参数来调用该方法。
如果创建了自己的助手,可以按照前面章节所述的提供相应的访问方法。