Zend Framework教程之Zend_Helpers动作助手ViewRenderer用法详解

时间:2021-05-25

本文实例讲述了Zend Framework教程之Zend_Helpers动作助手ViewRenderer用法。分享给大家供大家参考,具体如下:

MVC结构中视图层和控制器的解耦,以及渲染。往往是重复或者冗余的工作。如果一个完善的框架,对MVC的使用,必定会对这些操作进行合理的设计。让开发者更专注内容而不是控制逻辑结构本身。在ZendFramework中,主要是通过动作助手ViewRenderer来完成这个操作的。ViewRenderer 自动的完成在控制器内建立视图对象并渲染视图的过程;

ViewRenderer

介绍

视图解析(ViewRenderer)助手为实现下列目标设计:

不需要在控制器内创建视图对象实例;视图对象将在控制器内自动注册。
根据当前的模块自动地设置视图脚本、助手、过滤器路径。指派当前的模块名为助手和过滤器类的类名前缀。
为所有分发的控制器和动作创建全局有效的视图对象。
允许开发人员为所有控制器设置默认的视图解析选项。
加入无需干预自动解析试图脚本的功能。
允许开发人员为视图基路径和视图脚本路径创建自己的规范。

Note: 如果手动执行_forward()、redirect、或者render时,不会发生自动解析。因为执行这些动作时,等于告诉ViewRenderer,你要自己确定输出结果。

Note: ViewRenderer助手默认启用。

你可以通过前端控制器的noViewRenderer方法、设定参数($front->setParam('noViewRenderer', true))或者从助手经纪人栈(helper broker stack)中移除助手(Zend_Controller_Action_HelperBroker::removeHelper('viewRenderer'))等方式禁用该助手。

如希望在分发前端控制器前修改ViewRenderer设定,可采用下面的两种方法:

创建实例并注册自己的ViewRenderer对象,然后传入到助手经纪人。

$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();$viewRenderer->setView($view) ->setViewSuffix('php');Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

通过助手经纪人即时的初始化并/或获取ViewRenderer对象。

$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');$viewRenderer->setView($view) ->setViewSuffix('php');

API

大多数使用中,只需要简单的创建 ViewRenderer对象,然后传入到动作助手经纪人。创建实例并注册的最简单方式是使用助手经纪人的getStaticHelper()方法:

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()方法。

例如这个类:

// 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()

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);

Note: setRender() 和 direct()并不会实际解析视图脚本,而是提示postDispatch()和postDispatch()解析视图。

构造函数允许可选的传入参数视图对象和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()获取当前规则。
为在路径规范之上精心设计的控制,可以使用Zend_Filter_Inflector。深入地,视图解析器(ViewRenderer)已经使用inflector来执行路径映射。为和inflector互动 - 或者设置你自己的或者修改缺省的inflector,下面的方法可以被使用:
getInflector() 将获取inflector。如果在视图解析器中不存在, 它用缺省的规则创建一个。
缺省地,它用静态规则引用和静态目标做为后缀和模块目录;这允许不同的视图解析器具备动态修改inflector能力的属性。
setInflector($inflector, $reference) 允许设置定制的inflector和视图解析器一起使用。如果$reference 是true,它将设置后缀和模块目录作为静态引用和目标给视图解析器 属性。

Note: 缺省查找约定(Conventions)

视图解析器做了一些路径标准化使视图脚本查找更容易。缺省规则如下:

:module: 混合词和驼峰词被短横线分开,并整个串变成小写。例如:"FooBarBaz" 变成 "foo-bar-baz"。
在内部,变形器(inflector) 使用过滤器Zend_Filter_Word_CamelCaseToDash 和 Zend_Filter_StringToLower。
:controller: 混合词和驼峰词被短横线分开;下划线转换成目录分隔符,并且整个串变小写。例如:"FooBar" becomes "foo-bar"; "FooBar_Admin" 变成 "foo-bar/admin".
在内部,inflector 使用过滤器Zend_Filter_Word_CamelCaseToDash、Zend_Filter_Word_UnderscoreToSeparator 和 Zend_Filter_StringToLower。
:action: 混合词和驼峰词被短横线分开;非字母数字字符翻译成短横线,并且整个串变成小写。 例如 "fooBar" 变成 "foo-bar"; "foo-barBaz" 变成 "foo-bar-baz"。

在内部,inflector 使用过滤器 Zend_Filter_Word_CamelCaseToDash、Zend_Filter_PregReplace 和 Zend_Filter_StringToLower。

视图解析器 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参数。

Note: 当视图已经被解析到响应对象,将会设置noRender阻止相同的脚本被多次解析。

Note: 默认的,Zend_Controller_Action::renderScript()代理ViewRenderer的renderScript()方法。

getViewScript($action, $vars)基于传入的动作和/或$vars中的变量创建到视图脚本的路径。该数组中的键可以包含所有的路径指定键('moduleDir','module', 'controller', 'action', and 'suffix')。传入的任何变量都会优先使用,否则利用基于当前请求的值。

getViewScript()根据noController标志的设定值使用viewScriptPathSpec或者viewScriptPathNoControllerSpec。

模块、控制器以及动作中的单词定界符将后替换成短线('-')。因此,控制器名称'foo.bar'和动作'baz:bat'按照默认的路径规则将会得到视图脚本路径'foo-bar/baz-bat.phtml'。

Note: 默认的,Zend_Controller_Action::getViewScript()代理ViewRenderer的getViewScript()方法。

render($action, $name, $noController)首先检查$name或 $noController参数是否传入,如果传入,则在ViewRenderer中设定相应的标志(分别是响应片段和noController)。然后传入$action参数到getViewScript(),最后传入计算的试图脚本路径到renderScript()。

Note: 注意使用render()的边际效应:传入的响应片段名称和noController标志在视图对象中存留。此外解析结束后noRender会被设置。

Note: 默认的,Zend_Controller_Action::render()代理 ViewRenderer的render()方法。

renderBySpec($action, $vars, $name)允许传入路径规则变量以确定创建的视图脚本路径。它把$action和$vars传入到getScriptPath(),将脚本路径结果和$name传入到renderScript()。

基础用法示例

Example #9 基本用法

大多数基础使用中,只需在bootstrap中使用助手经纪人简单的初始化和注册ViewRenderer 助手,然后在动作方法中设置变量。

// 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'); }}

Note: 命名规则:控制器和动作名中的单词定界符

如果控制器或者动作名称由几个单词组成,分发器要求在URL中使用特定的路径和单词定界符分隔。ViewRenderer创建路径时将控制器名称中的任何路径定界符替换成实际的路径定界符('/'),任何单词定界符替换成短线('-')。对动作/foo.bar/baz.bat的调用将分发到FooBarController.php中的FooBarController::bazBatAction(),然后解析foo-bar/baz-bat.phtml;对动作/bar_baz/baz-bat的调用将分发到Bar/BazController.php中的Bar_BazController::bazBatAction(),并解析bar/baz/baz-bat.phtml。

注意到在第二个例子中,模块依然是默认的模块,但由于路径分隔符的存在,控制器的接收到的名字为Bar_BazController,该类在文件Bar/BazController.php中。ViewRenderer模拟了控制器的目录分层。

Example #10 禁用自动解析

对于某些动作和控制器,可能希望关闭自动解析——例如,如果想发送其他类型的输出(XML,JSON等),或者更简单的不想发送任何东西。有两个选项:关闭所有的自动解析(setNeverRender()),或者仅仅关闭当前动作的自动解析(setNoRender())。

// Baz controller class, bar module:class Bar_BazController extends Zend_Controller_Action{ public function fooAction() { // Don't auto render this action <strong>$this->_helper->viewRenderer->setNoRender();</strong> }}// 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(); }}

Note: 大多数情况下,全局的关闭自动解析(setNeverRender())没有意义,因为这样ViewRenderer做的唯一件事只是自动设置了视图对象。
Example #11 选择另外的视图脚本
有些情况下需要解析另一个脚本而非以动作命名的脚本。例如,如果你有一个控制器包含增加和编辑两个动作,它们可能都显示相同的'form'视图,尽管拥有不同的值集合(value set)。只需要使用setScriptAction()或者setRender()简单的改变脚本的名称,或者以成员方法的形式调用助手,它将调用setRender()。

// 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
如果需要修改视图对象怎么办——例如改变助手路径或者编码?可以在控制器中修改视图对象设定,或者从ViewRenderer中抓取视图对象;两种方式引用的是同一个对象。

// 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 一个动作中解析多个视图脚本

有时可能需要在一个动作中解析多个视图脚本。这个非常简单,多次调用render()就行了:

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 }}

ViewRenderer的相关源码如下,仔细分析,并不难看出实现方法:

<?php/** * @see Zend_Controller_Action_Helper_Abstract */require_once 'Zend/Controller/Action/Helper/Abstract.php';/** * @see Zend_View */require_once 'Zend/View.php';/** * View script integration * * Zend_Controller_Action_Helper_ViewRenderer provides transparent view * integration for action controllers. It allows you to create a view object * once, and populate it throughout all actions. Several global options may be * set: * * - noController: if set true, render() will not look for view scripts in * subdirectories named after the controller * - viewSuffix: what view script filename suffix to use * * The helper autoinitializes the action controller view preDispatch(). It * determines the path to the class file, and then determines the view base * directory from there. It also uses the module name as a class prefix for * helpers and views such that if your module name is 'Search', it will set the * helper class prefix to 'Search_View_Helper' and the filter class prefix to ; * 'Search_View_Filter'. * * Usage: * <code> * // In your bootstrap: * Zend_Controller_Action_HelperBroker::addHelper(new Zend_Controller_Action_Helper_ViewRenderer()); * * // In your action controller methods: * $viewHelper = $this->_helper->getHelper('view'); * * // Don't use controller subdirectories * $viewHelper->setNoController(true); * * // Specify a different script to render: * $this->_helper->viewRenderer('form'); * * </code> * * @uses Zend_Controller_Action_Helper_Abstract * @package Zend_Controller * @subpackage Zend_Controller_Action_Helper * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://pact('module', 'controller', 'action'); foreach ($vars as $key => $value) { switch ($key) { case 'module': case 'controller': case 'action': case 'moduleDir': case 'suffix': $params[$key] = (string) $value; break; default: break; } } if (isset($params['suffix'])) { $origSuffix = $this->getViewSuffix(); $this->setViewSuffix($params['suffix']); } if (isset($params['moduleDir'])) { $origModuleDir = $this->_getModuleDir(); $this->_setModuleDir($params['moduleDir']); } $filtered = $inflector->filter($params); if (isset($params['suffix'])) { $this->setViewSuffix($origSuffix); } if (isset($params['moduleDir'])) { $this->_setModuleDir($origModuleDir); } return $filtered; } /** * Render a view script (optionally to a named response segment) * * Sets the noRender flag to true when called. * * @param string $script * @param string $name * @return void */ public function renderScript($script, $name = null) { if (null === $name) { $name = $this->getResponseSegment(); } $this->getResponse()->appendBody( $this->view->render($script), $name ); $this->setNoRender(); } /** * Render a view based on path specifications * * Renders a view based on the view script path specifications. * * @param string $action * @param string $name * @param boolean $noController * @return void */ public function render($action = null, $name = null, $noController = null) { $this->setRender($action, $name, $noController); $path = $this->getViewScript(); $this->renderScript($path, $name); } /** * Render a script based on specification variables * * Pass an action, and one or more specification variables (view script suffix) * to determine the view script path, and render that script. * * @param string $action * @param array $vars * @param string $name * @return void */ public function renderBySpec($action = null, array $vars = array(), $name = null) { if (null !== $name) { $this->setResponseSegment($name); } $path = $this->getViewScript($action, $vars); $this->renderScript($path); } /** * postDispatch - auto render a view * * Only autorenders if: * - _noRender is false * - action controller is present * - request has not been re-dispatched (i.e., _forward() has not been called) * - response is not a redirect * * @return void */ public function postDispatch() { if ($this->_shouldRender()) { $this->render(); } } /** * Should the ViewRenderer render a view script? * * @return boolean */ protected function _shouldRender() { return (!$this->getFrontController()->getParam('noViewRenderer') && !$this->_neverRender && !$this->_noRender && (null !== $this->_actionController) && $this->getRequest()->isDispatched() && !$this->getResponse()->isRedirect() ); } /** * Use this helper as a method; proxies to setRender() * * @param string $action * @param string $name * @param boolean $noController * @return void */ public function direct($action = null, $name = null, $noController = null) { $this->setRender($action, $name, $noController); }}

更多关于zend相关内容感兴趣的读者可查看本站专题:《Zend FrameWork框架入门教程》、《php优秀开发框架总结》、《Yii框架入门及常用技巧总结》、《ThinkPHP入门教程》、《php面向对象程序设计入门教程》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》

希望本文所述对大家基于Zend Framework框架的PHP程序设计有所帮助。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章