动作助手介绍动作助手可以向任何Zend_Controller_Action的衍生动作控制器中,即时的加入功能(runtime and/or on-demand functionality),以使得增加公共的动作控制器功能时,尽量减少衍生动作控制器类的必要。
动作助手有多种使用方式。动作助手使用了一套经纪系统(brokerage system),与Zend_View_Helper中使用的,也就是Zend_Controller_Plugin的经纪系统类似。动作助手(like 初始化助手根据需求以及助手的功能,可有几种不同的初始化方式。
助手经纪人(broker)存储在
也可以显式的实例化助手。如果你要在动作控制器之外使用助手,或者给助手经纪人传入一个助手供所有的动作使用,你可能希望直接这么做。实例化助手和实例化其他PHP类的方法一样。 助手经纪人
使用
Zend_Controller_Action_HelperBroker::addHelper($helper);
实例化助手并传入经纪人有点耗费时间和资源,不过
这些方法是静态的,因而可以根据需要在控制器链中的任何位置调用动态的加载助手。
使用
// Check if 'redirector' helper is registered with the broker:
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
echo 'Redirector helper registered';
}
从助手经纪人中获取助手有两个静态方法:
两个方法都带一个参数,
// Check if 'redirector' helper is registered with the broker, and fetch:
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
$redirector =
Zend_Controller_Action_HelperBroker::getExistingHelper('redirector');
}
// Or, simply retrieve it, not worrying about whether or not it was
// previously registered:
$redirector =
Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
}
最后,使用
// Conditionally remove the 'redirector' helper from the broker:
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
Zend_Controller_Action_HelperBroker::removeHelper('redirector')
}
内建的动作助手
Zend Framework 默认包含若干个动作助手: 动作堆栈(助手)
Example #1 用动作、控制器和模块名来添加一个任务
经常地,仅仅指定动作、控制器和模块(和可选的参数)最简单,和调用
class FooController extends Zend_Controller_Action
{
public function barAction()
{
// Add two actions to the stack
// Add call to /foo/baz/bar/baz
// (FooController::bazAction() with request var bar == baz)
$this->_helper->actionStack('baz',
'foo',
'default',
array('bar' => 'baz'));
// Add call to /bar/bat
// (BarController::batAction())
$this->_helper->actionStack('bat', 'bar');
}
}
Example #2 使用请求对象添加一个任务
有时候请求对象的OOP本性很有用;你也可以传递这样一个对象给
class FooController extends Zend_Controller_Action
{
public function barAction()
{
// Add two actions to the stack
// Add call to /foo/baz/bar/baz
// (FooController::bazAction() with request var bar == baz)
$request = clone $this->getRequest();
// Don't set controller or module; use current values
$request->setActionName('baz')
->setParams(array('bar' => 'baz'));
$this->_helper->actionStack($request);
// Add call to /bar/bat
// (BarController::batAction())
$request = clone $this->getRequest();
// don't set module; use current value
$request->setActionName('bat')
->setControllerName('bar');
$this->_helper->actionStack($request);
}
}
AutoComplete
许多 AJAX javascript 函数库提供了以潜在地匹配结果的选择列表作为用户类别显示的自动完成。
因为并非所有的 JS 库用同样的方法实现自动完成, 每个实现的基本用法都一样:
class FooController extends Zend_Controller_Action
{
public function barAction()
{
// Perform some logic...
// Encode and send response;
$this->_helper->autoCompleteDojo($data);
// Or explicitly:
$response = $this->_helper->autoCompleteDojo
->sendAutoCompletion($data);
// Or simply prepare autocompletion response:
$response = $this->_helper->autoCompleteDojo
->prepareAutoCompletion($data);
}
}
缺省地,自动完成做这些工作:
可用的助手方法包括:
目前 AutoCompletion with DojoDojo 本身没有 AutoCompletion 小部件,但有两个小部件可以执行 AutoCompletion:ComboBox 和 FilteringSelect。对于这两者,都要求实现 QueryReadStore 的数据存储,关于这话题的更多信息参见 » dojo.data 文档。 在 Zend Framework 中,可以传递简单的索引的数组给 AutoCompleteDojo 助手,它将返回一个适合和这样一个存储一起使用的 JSON 响应:
// within a controller action:
$this->_helper->autoCompleteDojo($data);
Example #3 AutoCompletion with Dojo Using Zend MVC AutoCompletion with Dojo 通过 Zend MVC 需要若干事项:为你想要 AutoCompletion 的 ComboBox 生成一个表单对象,服务于 AutoCompletion 结果的控制器动作,生成定制的 QueryReadStore 来连接到 AutoCompletion 动作和 javascript 的生成用于在服务器端初始化 AutoCompletion。 首先,看一下必需的 javascript 。Dojo 为生成 OOP javascript 提供一个完整的框架,很像 Zend Framework 对于 PHP。它的部分功能是使用目录等级结构生成假的命名空间(pseudo-namespaces )。 我们将在和 Dojo 同一级目录创建一个 'custom' 目录,那是 Dojo 发行的一部分。 在目录里面,我们将创建 javascript 文件,TestNameReadStore.js 带有以下内容:
dojo.provide("custom.TestNameReadStore");
dojo.declare("custom.TestNameReadStore", dojox.data.QueryReadStore, {
fetch:function (request) {
request.serverQuery = { test:request.query.name };
return this.inherited("fetch", arguments);
}
});
该类是 Dojo 自己的 QueryReadStore 的扩展,QueryReadStore 是一个抽象类。我们简单地通过请求定义一个方法,并把它分配给 'test' 元素。 下一步,为我们想要的 AutoCompletion 生成表单元素:
class TestController extends Zend_Controller_Action
{
protected $_form;
public function getForm()
{
if (null === $this->_form) {
$this->_form = new Zend_Form();
$this->_form->setMethod('get')
->setAction(
$this->getRequest()->getBaseUrl() . '/test/process'
)
->addElements(array(
'test' => array('type' => 'text', 'options' => array(
'filters' => array('StringTrim'),
'dojoType' => array('dijit.form.ComboBox'),
'store' => 'testStore',
'autoComplete' => 'false',
'hasDownArrow' => 'true',
'label' => 'Your input:',
)),
'go' => array('type' => 'submit',
'options' => array('label' => 'Go!'))
));
}
return $this->_form;
}
}
这里,我们用 'test' 和 'go' 方法生成表单。'test' 方法添加若干特别的 Dojo 专用的属性:dojoType、 store、 autoComplete 和 hasDownArrow。dojoType 用来指示我们在生成 comboBox,并且我们将把它链接到 'testStore' 的数据存储(键 'store')-- 稍后还有更多。指定 'autoComplete' 作为 false 告诉 Dojo 不要自动选择第一个匹配,但是要显示一个匹配列表。最后,'hasDownArrow' 生成和选择 box 类似的向下箭头,这样我们可以显示和隐藏匹配。 让我们添加一个方法来显示表单,和处理 AutoCompletion 的结束点:
class TestController extends Zend_Controller_Action
{
// ...
/**
* Landing page
*/
public function indexAction()
{
$this->view->form = $this->getForm();
}
public function autocompleteAction()
{
if ('ajax' != $this->_getParam('format', false)) {
return $this->_helper->redirector('index');
}
if ($this->getRequest()->isPost()) {
return $this->_helper->redirector('index');
}
$match = trim($this->getRequest()->getQuery('test', ''));
$matches = array();
foreach ($this->getData() as $datum) {
if (0 === strpos($datum, $match)) {
$matches[] = $datum;
}
}
$this->_helper->autoCompleteDojo($matches);
}
}
在 既然我们在后台有了所有的东西,来看一下在视图脚本中对于 landing 页面我们需要提交什么。 首先,我们需要设置数据存储,然后解析表单,最后确保合适的 Dojo 库被加载 --包括定制的数据存储。来看看视图脚本,步骤在注释里:
<? // setup our data store: ?>
<div dojoType="custom.TestNameReadStore" jsId="testStore"
url="<?= $this->baseUrl() ?>/unit-test/autocomplete/format/ajax"
requestMethod="get"></div>
<? // render our form: ?>
<?= $this->form ?>
<? // setup Dojo-related CSS to load in HTML head: ?>
<? $this->headStyle()->captureStart() ?>
@import "<?= $this->baseUrl() ?>/javascript/dijit/themes/tundra/tundra.css";
@import "<?= $this->baseUrl() ?>/javascript/dojo/resources/dojo.css";
<? $this->headStyle()->captureEnd() ?>
<? // setup javascript to load in HTML head, including all required
// Dojo libraries: ?>
<? $this->headScript()
->setAllowArbitraryAttributes(true)
->appendFile($this->baseUrl() . '/javascript/dojo/dojo.js',
'text/javascript',
array('djConfig' => 'parseOnLoad: true'))
->captureStart() ?>
djConfig.usePlainJson=true;
dojo.registerModulePath("custom","../custom");
dojo.require("dojo.parser");
dojo.require("dojox.data.QueryReadStore");
dojo.require("dijit.form.ComboBox");
dojo.require("custom.TestNameReadStore");
<? $this->headScript()->captureEnd() ?>
注意对视图助手的调用如 headStyle 和 headScript,它们是占位符,我们可以在布局视图脚本的 HTML 头中解析。 现在所有的 Dojo AutoCompletion 开始工作了。 AutoCompletion with Scriptaculous» Scriptaculous 需要一个特定格式的 HTML 响应。 和这个库一起使用的助手是 'AutoCompleteScriptaculous',给它提供一个数据数组,这个助手将生成兼容于 Ajax.Autocompleter 的 HTML 响应。 ContextSwitch and AjaxContext
你必须在关于动作能够响应哪个上下文(context)的控制器中打开(enable)其中一个。如果一个进来的请求对给定的动作指出一个有效的上下文,助手将做:
例子,考虑下列的控制器:
class NewsController extends Zend_Controller_Action
{
/**
* Landing page; forwards to listAction()
*/
public function indexAction()
{
$this->_forward('list');
}
/**
* List news items
*/
public function listAction()
{
}
/**
* View a news item
*/
public function viewAction()
{
}
}
我们想让
class NewsController extends Zend_Controller_Action
{
public function init()
{
$contextSwitch = $this->_helper->getHelper('contextSwitch');
$contextSwitch->addActionContext('list', 'xml')
->initContext();
}
// ...
}
这将完成:
现在你需要创建一个新的视图脚本 'news/list.xml.phtml',它将创建和解析 XML。 为决定一个请求是否应该初始化一个上下文开关(context switch),这个助手检查在请求对象理的令牌。缺省地,它寻找一个 'format' 参数,尽管这是可配置的。这意味着,在大多数情况下,为触发一个上下文开关,你可以添加一个 'format' 参数给你的请求:
缺省可用的上下文
缺省地,
创建定制的上下文
有时候,缺省的上下文不够用,例如你需要返回 YAML,或系列化 PHP、RSS 或 ATOM feed 等等,
最容易的添加新的上下文的办法是通过
和上下文交互作用的方法:
为每个动作设置上下文
有两个设置可用的上下文的机制:或者在控制器里手工创建数组,或者使用在
添加动作/上下文关系的基本方法是
class FooController extends Zend_Controller_Action
{
public function listAction()
{
}
public function viewAction()
{
}
public function commentsAction()
{
}
public function updateAction()
{
}
}
假如我们想添加 XML 上下文到 'list'动作、XML 和 JSON 上下文到 'comments' 动作,可以在
class FooController extends Zend_Controller_Action
{
public function init()
{
$this->_helper->contextSwitch()
->addActionContext('list', 'xml')
->addActionContext('comments', array('xml', 'json'))
->initContext();
}
}
另外,还可以定义数组属性
class FooController extends Zend_Controller_Action
{
public $contexts = array(
'list' => array('xml'),
'comments' => array('xml', 'json')
);
public function init()
{
$this->_helper->contextSwitch()->initContext();
}
}
上述不但缺少周密考虑,而且有潜在的错误。 下面的方法可用来构造上下文映射:
初始化上下文开关
为初始化上下文开关,需要在动作控制器中调用
class NewsController extends Zend_Controller_Action
{
public function init()
{
$this->_helper->contextSwitch()->initContext();
}
}
在某些情况下,你想强制使用上下文,例如,如果上下文开关是激活状态,你只想用 XML 上下文,可以通过传递上下文给
$contextSwitch->initContext('xml');
另外的功能
用来改变
AjaxContext 函数
首先,它为确定上下文使用不同的动作控制器属性 -
其次,由请求对象的
第三, Example #4 允许动作响应 Ajax 的请求 在下面的例子中,我们允许对动作 'view'、 'form' 和 'process' 的请求响应 AJAX 的请求。在头两个例子 'view' 和 'form',返回不更新页面的 HTML 片段;在最后的例子,返回 JSON。
class CommentController extends Zend_Controller_Action
{
public function init()
{
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('view', 'html')
->addActionContext('form', 'html')
->addActionContext('process', 'json')
->initContext();
}
public function viewAction()
{
// Pull a single comment to view.
// When AjaxContext detected, uses the comment/view.ajax.phtml
// view script.
}
public function formAction()
{
// Render the "add new comment" form.
// When AjaxContext detected, uses the comment/form.ajax.phtml
// view script.
}
public function processAction()
{
// Process a new comment
// Return the results as JSON; simply assign the results as
// view variables, and JSON will be returned.
}
}
在客户端,AJAX 库将请求终点 '/comment/view'、 '/comment/form' 和 '/comment/process',并传递 'format' 参数:'/comment/view/format/html'、'/comment/form/format/html' 和 '/comment/process/format/json'。(或者你可以通过查询字符串( query string) 传递参数,如:"?format=json") 假定你的库传递 'X-Requested-With:XmlHttpRequest'头,这些动作将返回适当的响应格式。 FlashMessenger简介
Basic Usage Example
下面的例子展示flash messenger最基本的用法。当动作
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();
}
}
JSON当处理期望数据表响应的 AJAX 请求,JSON 响应迅速变成选择的响应。JSON 可以立即在客户端被解析,从而快速执行。 JSON 动作助手完成以下任务:
用法很简单:或者把它作为助手代理的方法来调用,或者调用
class FooController extends Zend_Controller_Action
{
public function barAction()
{
// do some processing...
// Send the JSON response:
$this->_helper->json($data);
// or...
$this->_helper->json->sendJson($data);
// or retrieve the json:
$json = $this->_helper->json->encodeJson($data);
}
}
转向器(Redirector)介绍
转向器 转向器拥有影响重定向行为的大量方法:
此外,转向器中还有大量方法来执行实际的重定向。
最后,你可以在任何时刻使用 基础用例Example #5 设定选项 这个例子改变了几个选项,包括设定重定向时使用的HTTP状态码为303,重定向时不默认退出,以及定义了默认的URL供重定向使用。
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)
->setGotoSimple("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
}
}
Example #6 使用默认设定 这个例子假定使用默认设定,也就意味着任何重定向将导致立即退出。
// 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
}
}
Example #7 使用
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->gotoSimple('my-action',
'my-controller',
null,
array('param1' => 'test',
'param2' => 'test2'
)
);
}
}
Example #8 通过
下面的例子使用了路由器的
$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
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介绍
视图解析(
API
大多数使用中,只需要简单的创建
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
动作控制器第一次实例化时,会触发
每次执行 例如这个类:
// 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:
$this->foo(); // call Foo_View_Helper_Foo::foo()
构造函数允许可选的传入参数视图对象和
$view = new Zend_View(array('encoding' => 'UTF-8'));
$options = array('noController' => true, 'neverRender' => true);
$viewRenderer =
new Zend_Controller_Action_Helper_ViewRenderer($view, $options);
还有几个额外的方法用来定制路径规则,供确定视图基路径来增加视图对象,确定视图脚本路径查找并解析视图脚本时使用。这些方法每个都带有下面一个或更多的占位符(placehodlers)。
控制器路径规则有关的方法:
为在路径规范之上精心设计的控制,可以使用Zend_Filter_Inflector。深入地,
基础用法示例Example #9 基本用法
大多数基础使用中,只需在bootstrap中使用助手经纪人简单的初始化和注册
// In your bootstrap:
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
...
// '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');
}
}
Example #10 禁用自动解析
对于某些动作和控制器,可能希望关闭自动解析——例如,如果想发送其他类型的输出(XML,JSON等),或者更简单的不想发送任何东西。有两个选项:关闭所有的自动解析(
// 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();
}
}
Example #11 选择另外的视图脚本
有些情况下需要解析另一个脚本而非以动作命名的脚本。例如,如果你有一个控制器包含增加和编辑两个动作,它们可能都显示相同的'form'视图,尽管拥有不同的值集合(value set)。只需要使用
// 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...
}
}
Example #12 修改注册的视图Modifying the registered view
如果需要修改视图对象怎么办——例如改变助手路径或者编码?可以在控制器中修改视图对象设定,或者从
// 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');
}
}
高级用法示例Example #13 修改路径规则 有些情况下,默认的路径规则可能并不适合站点的需要。比如,希望拥有一个单独的模板树供设计人员访问(例如,如果你使用» Smarty,这是很典型的情形)。这种情况下,你可能想硬编码视图的基路径规则,为动作视图脚本路径自身创建一套规则。 假定视图的基路径(base path)为'/opt/vendor/templates',希望通过':moduleDir/:controller/:action.:suffix'引用视图脚本;如果设定了noController标志,想在顶级而不是在子目录中解析(':action.:suffix')。最终希望使用'tpl'作为视图脚本文件的后缀。
/**
* 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);
Example #14 一个动作中解析多个视图脚本
有时可能需要在一个动作中解析多个视图脚本。这个非常简单,多次调用
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
}
}
编写自己的助手
动作助手继承抽象类
助手类中还可以包含一个
// Redirect to /blog/view/item/id/42
$this->_helper->redirector('item', 'view', 'blog', array('id' => 42));
在内部,助手经纪人的 如果创建了自己的助手,可以按照前面章节所述的提供相应的访问方法。
|