视图
部分内容包括:视图,模板,视图帮助类等。
6.0视图介绍
在Zendframework的MVC编程模型中,视图(View)是在控制器的控制和指挥下,用来对程序逻辑进行呈现(Render)的。呈现的结果,就是我们在浏览器里看到的文字、图片、表单等各种网页元素及其字体、颜色、样式等各种效果。
Zend_View Class就是负责视图工作的类,它有效地完成了视图与程序逻辑的分离。它提供了视图帮助、输出过滤和变量转义等功能。
Zend_View还是一个模板系统,我们可以用php作为我们的模板语言。当然ZF还可以在View脚本里使用其他第三方的模板系统,比如PHPLib和Smarty等。
使用Zend_View时主要分两步,首先声明一个Zend_View实例,把变量等赋给它,然后使用控制脚本,根据视图脚本呈现出结果。
例如:
控制脚本(在控制器文件的action里,例如IndexController.php中的函数dispdataAction()):……
Function dispdataAction()
{
……
$data = ‘to view’; //数据变量
Zend_Loader::loadClass(’Zend_View’);
$view = new Zend_View(); //实例化
$view->books = $data; //赋值
echo $view->render(’view.php’);
}
视图脚本转义输出语句(在视图脚本文件里,本例是view.php):
php echo $this->escape($this->data
?>
6.1引导文件中setParam(’noViewRenderer’,
Index文件中$fc->setParam(’noViewRenderer’,
false 是noViewRenderer的默认值。也就是说,如果没有该语句,则认为noViewRenderer参数被设置为false。
noViewRenderer参数被设置为false:意味着控制器使用ZF默认的视图特性,比如视图文件默认必须存放于views\script\
noViewRenderer参数被设置为true:意味着控制器不使用ZF默认的视图特性,而是通过显式的实例化Zend_view对象,通过我们自己的代码来设置视图对象的属性和方法。本部分“视图介绍”中就是显式声明和使用视图对象的例子。为了程序的灵活性和可控行,我们自然建议把noViewRenderer 设置为 true,这也是比较通常的做法。
6.2视图对象的Options
视图对象的Options选项进一步规定了视图脚本呈现过程中的一些细节。这些选项可以通过在声明视图对象时指定,在构造函数里设置,也可以通过set……()方法来指定。
—basePath基本路径
设置方法:setBasePath(), addBasePath()
例如目录结构:base/path/
helpers/
filters/
scripts/
用$view->setBasePath(”base/ path/”);语句设置基本路径后,在没有$view->setScriptPath(’……’);语句直接指定脚本路径时,就会自动在base/path/scripts/下搜索视图脚本文件,如果使用了视图助手和过滤器,就会分别自动在helpers/和 filters/文件夹下搜索。
—encoding字符编码
用来在使用htmlentities()、htmlspecialchars()或其他操作时,指定字符编码。
设置方法:setEncoding()
默认编码是ISO-8859-1 (latin1)。
—escape回调函数
用于在视图呈现时调用该函数。后边有示例。
设置方法:setEscape()
—filter过滤器
用于在视图呈现后调用过滤方法。
设置方法:setFilter(), addFilter(),
—strictVars
当视图视图发送一个未初始化的变量时,用该选项指定ZF收购给出一个提示或警告信息:
Notice: Key “xxx” does not exist ……
设置方法:strictVars(true)
6.3视图对象的一些属性存取方法
—getVars() 得到所有赋予的变量
—clearVars()清除所有赋予的变量
—getScriptPath($script) 得到给定脚本的路径
—getScriptPaths()得到所有脚本的路径
—getHelperPath($helper)得到某个指定助手类的路径
—getHelperPaths()得到所有助手的路径
—getFilterPath($filter) 得到某个指定过滤器类的路径
—getFilterPaths()得到所有过滤器的路径
6.4视图的路径:
6.4.1视图脚本的搜寻路径
如果引导文件中$fc->setParam(’noViewRenderer’, false);
则默认指定视图文件views/scripts/[controller_name]/[action].phtml
在实际的程序代码中,为了获得可定制的灵活性,都在控制器中实际指定了视图文件的路径:
指定路径例句:$view->setScriptPath(’…/views’);或
$view->addScriptPath(’…/views’);
这个时候,引导文件必须有$fc->setParam(’noViewRenderer’, true);语句,即设置noViewRenderer为true。
6.4.2视图脚本的搜寻的优先顺序$view = new Zend_View();
$view->addtScriptPath(’…/views1′);
$view->addScriptPath(’…/views2′);
$view->addScriptPath(’…/views3′);
Zend的手册是这样说的:“如果没有指定任何搜素路径,则在控制器文件下搜索视图文件。”但是通过实际环境测试,发现这时会报告错误:
“no view script directory set; unable to determine location for view script”
看起来最少需要指定一个搜索路径。
如果指定了多条搜素路径,则最后的搜索路径优先。也就是说,如果所有的搜索路径下有相同的视图文件,则最后路径下的起作用,它覆盖了前边路径下的视图文件。
6.5视图控制脚本及其变量传递
ZF的控制器是实例化和设置Zend_View的地方。在这里,我们给视图赋值并告诉它用指定的视图脚本去呈现它们。
6.5.1给视图对象赋值
示例:$view->Variable=”……”;的形式
function assign1Action()
{
$view = new Zend_View();
$view->setScriptPath(’views’);
$view->strictVars(true);
$view->a = “Hay”;
$view->b = “Bee”;
$view->c = “Sea”;
$view->d;
echo $view->render(’tp_abc.php’);
}
示例:
$view-> assign(’ Variable ‘, “……”);的形式function assign2Action()
{
$view = new Zend_View();
$view->setScriptPath(’views’);
$view->assign(’a', “Hay”);
$view->assign(’b', “Bee”);
$view->assign(’c', “Sea”);
echo $view->render(’tp_abc.php’);
}
示例:数组function assign3Action()
{
$view = new Zend_View();
$view->setScriptPath(’views’);
$array = array(
‘a’ => “Hay”,
‘b’ => “Bee”,
‘c’ => “Sea”,
);
$view->assign($array);
echo $view->render(’tp_abc.php’);
}
示例:对象function assign4Action()
{
$view = new Zend_View();
$view->setScriptPath(’views’);
$obj = new StdClass;
$obj->a = “Hay”;
$obj->b = “Bee”;
$obj->c = “Sea”;
$view->assign((array) $obj);
echo $view->render(’tp_abc.php’);
}
示例:
使用回调函数。function myhtmlentityAction()
{
$view = new Zend_View();
$view->setScriptPath(’views’);
//自定义类,在/models文件夹下的myclass.php文件中定义
$mycls = new myClass();
//调用$mycls类的myHtmlEntity方法
$view->setEscape(array($mycls, ‘myHtmlEntity’));
$obj = new StdClass;
$obj->a = “the Words hay bee sea “;
$obj->b = “this is bee”;
$obj->c = ” this is sea”;
$view->assign((array) $obj);
echo $view->render(’tp_abc.php’);
}
tp_abc.php视图脚本模板文件内容:
echo ‘a = ‘ . $this->escape($this->a).’
’;
echo ‘b = ‘ . $this->escape($this->b).’
’;
echo ‘c = ‘ . $this->escape($this->c).’
’;
?>
myclass.php文件内容:
class myClass
{
public function __construct($options = null)
{
}
function myHtmlEntity($val)
{//把所有单词首字母变为大写
return ucwords($val);
}
}
?>
输出结果:
a = The Words Hay Bee Sea
b = This Is Bee
c = This Is Sea
6.6视图脚本的变量转义输出(escaping output)
视图脚本得到变量以后,需要通过转义进行输出,变成页面可以显示的Html代码。
输出语句的格式:
echo $this->escape($this->variable);
$variable变量是在视图脚本里用render方法传递过来的。
一般情况下,传递的变量是通过PHP的 htmlspecialchars()函数转义的。而我们也可以实现我们自己的转义函数。请参考以上“使用回调函数”示例。
6.7视图脚本的模板系统—操作PHPLib类型的模板
模板系统进一步完美的实现了视图与程序逻辑的分离。视图脚本可以完美的操作PHPLib等类型的模板。
6.7.1PHPlib的安装和调用
为了测试下面的示例,我们必须安装PHPLib模板系统到我们的环境中。从网上下载到phplib-7.4.ZIP安装压缩包,解压到安装ZEND的library文件夹下,就完成了安装。
为了在ZF的视图脚本里调用得到模板类文件,必须在引导文件Index.php的set_include_path部分添加PHPLib模板类库文件夹phplib-7.4/php到搜索路径中。以下示例同时包含了Smarty模板引擎的类库文件的搜索路径:set_include_path(’.’ .
PATH_SEPARATOR . ‘../library/’.
PATH_SEPARATOR . ‘../library/phplib-7.4/php/’.
PATH_SEPARATOR . ‘../library/Smarty-2.6.19/libs/’.
PATH_SEPARATOR . ‘models/’.
PATH_SEPARATOR . get_include_path()
);
注意,所有路径都是以引导文件所在文件夹作为参照的。尽管视图文件里所在文件夹不是引导文件所在根目录,但在视图文件里包含PHPLib类库文件的语句include_once ‘template.inc’;仍然是以引导文件所在目录作为参照的。
6.7.2在视图文件里调用PHPLib模板
首先包含PHPLib类库文件,然后声明模板类的一个实例。使用模板类,首先需要指定一些属性,比如指定模板所在路径,指定模板文件等,然后用set_var传递模板变量,最后用parse方法调用模板文件。PHPLib模板系统的详细用法请参考其帮助文档。
示例:
include_once ‘template.inc’;
$tpl = new Template();
$tpl->set_root(’views’);
if ($this->books)
{
$tpl->set_file(array(
“booklist” => “booklist.tpl”,
“eachbook” => “eachbook.tpl”,
));
foreach ($this->books as $key => $val)
{
$tpl->set_var(’author’, $this->escape($val['author']));
$tpl->set_var(’title’, $this->escape($val['title']));
$tpl->parse(”books”, “eachbook”, true);
}
$tpl->pparse(”output”, “booklist”);
}
else
{
$tpl->setFile(”nobooks”, “nobooks.tpl”);
$tpl->pparse(”output”, “nobooks”);
}
?>
booklist.tpl文件内容:
if ($this->books):
?>
作者 | 书名 |
---|---|
escape($val['author']) ?> | escape($val['title']) ?> |
else:
?>
There are no books to display.
endif;
eachbook.tpl文件内容:
/* Set the path to view scripts/templates */
public function setScriptPath($path);
/* Set a base path to all view resources */
public function setBasePath($path, $PRefix = ‘Zend_View’);
/* Add an additional base path to view resources */
public function addBasePath($path, $prefix = ‘Zend_View’);
/* Retrieve the current script paths */
public function getScriptPaths();
/* Overloading methods for assigning template variables as object properties */
public function __set($key, $value);
public function __get($key);
public function __isset($key);
public function __unset($key);
/* Manual assignment of template variables, or ability to assign multiple
variables en masse.*/
public function assign($spec, $value = null);
/* Unset all assigned template variables */
public function clearVars();
/* Render the template named $name */
public function render($name);
使用该接口,我们可以很容易的把第三方的模板引擎,比如Smarty,包装成Zend_View兼容的模板类。
6.8.1Smarty的安装
下载Smarty软件包,解压到ZEND的library文件夹下,就完成了安装。
为了在ZF的视图脚本里调用得到模板类文件,必须在引导文件Index.php的set_include_path部分添加Smarty模板类库文件夹Smarty-2.6.19/libs到搜索路径中,参看前面PHPlib的安装说明部分。
Smarty模板引擎需要建立template_dir和compile_dir文件夹才能工作。ZF手册里的示例因为缺少这些设置而无法运行,正确的代码片段如下:public function setScriptPath($path)
{
if (is_readable($path))
{
$this->_smarty->template_dir = $path;
$this->_smarty->compile_dir = $path; //必须加语句:设置编译路径
$this->_smarty->cache_dir = $path;//设置缓存路径
return;
}
……
我们把对Zend_View_Interface接口的实现的类,放在models文件夹下的ZendViewSmarty.php文件中,该文件的内容如下:
require_once ‘Zend/View/Interface.php’;
require_once ‘Smarty.class.php’;
class ZendViewSmarty implements Zend_View_Interface
{
/**
* Smarty object
* @var Smarty
*/
protected $_smarty;
/**
* Constructor
* @param string $tmplPath
* @param array $extraParams
* @return void
*/
public function __construct($tmplPath = null, $extraParams = array())
{
$this->_smarty = new Smarty;
if (null !== $tmplPath) {
$this->setScriptPath($tmplPath);
}
foreach ($extraParams as $key => $value) {
$this->_smarty->$key = $value;
}
}
/**
* Return the template engine object
* @return Smarty
*/
public function getEngine()
{
return $this->_smarty;
}
/**
* Set the path to the templates
* @param string $path The directory to set as the path.
* @return void
*/
public function setScriptPath($path)
{
if (is_readable($path)) {
$this->_smarty->template_dir = $path;
$this->_smarty->compile_dir = $path;
$this->_smarty->cache_dir = $path;
return;
}
throw new Exception(’Invalid path provided’);
}
/**
* Retrieve the current template directory
* @return string
*/
public function getScriptPaths()
{
return array($this->_smarty->template_dir);
}
/**
* Alias for setScriptPath
* @param string $path
* @param string $prefix Unused
* @return void
*/
public function setBasePath($path, $prefix = ‘Zend_View’)
{
return $this->setScriptPath($path);
}
/**
* Alias for setScriptPath
* @param string $path
* @param string $prefix Unused
* @return void
*/
public function addBasePath($path, $prefix = ‘Zend_View’)
{
return $this->setScriptPath($path);
}
/**
* Assign a variable to the template
* @param string $key The variable name.
* @param mixed $val The variable value.
* @return void
*/
public function __set($key, $val)
{
$this->_smarty->assign($key, $val);
}
/**
* Retrieve an assigned variable
* @param string $key The variable name.
* @return mixed The variable value.
*/
public function __get($key)
{
return $this->_smarty->get_template_vars($key);
}
/**
* Allows testing with empty() and isset() to work
* @param string $key
* @return boolean
*/
public function __isset($key)
{
return (null !== $this->_smarty->get_template_vars($key));
}
/**
* Allows unset() on object properties to work
* @param string $key
* @return void
*/
public function __unset($key)
{
$this->_smarty->clear_assign($key);
}
/**
* Assign variables to the template
* Allows setting a specific key to the specified value, OR passing an array
* of key => value pairs to set en masse.
* @see __set()
* @param string|array $spec The assignment strategy to use (key or array of key
* => value pairs)
* @param mixed $value (Optional) If assigning a named variable, use this
* as the value.
* @return void
*/
public function assign($spec, $value = null)
{
if (is_array($spec)) {
$this->_smarty->assign($spec);
return;
}
$this->_smarty->assign($spec, $value);
}
/**
* Clear all assigned variables
* Clears all variables assigned to Zend_View either via {@link assign()} or
* property overloading ({@link __get()}/{@link __set()}).
* @return void
*/
public function clearVars()
{
$this->_smarty->clear_all_assign();
}
/**
* Processes a template and returns the output.
* @param string $name The template to process.
* @return string The output.
*/
public function render($name)
{
return $this->_smarty->fetch($name);
}
}
?>
控制脚本中对我们我的模板类的调用代码:function smartyAction()
{
$view = new ZendViewSmarty(); //实例化新的模板类
$view->setScriptPath(’views’); //设置模板文件路径
$view->book = ‘Enter Zend Framework Programme’; //传递变量给模板引擎
$view->author = ‘张庆(网眼)’;
echo $view->render(’bookinfo.tpl’); //视图呈现
}
我们看到,由于Smarty模板引擎的良好特性,除过实现上述接口的代码比较复杂以外,我们这里的控制代码要比应用PHPLib模板简单得多。我们的数据不必像PHPLib引擎那样,要把数据传给视图脚本,再由视图脚本声明PHPLib类,再把数据发送给模板去呈现。这里是在控制脚本里把数据直接传递给Smarty模板去呈现的。这是因为ZendViewSmarty实现的是Zend_View接口,它和Zend_View的用法是一样的。
6.9视图助手(Helper)
视图脚本里经常有一些繁杂的事情,比如格式化日期、产生表单元素等等。这些可以用助手帮我们来完成。
助手类其实是一些以Zend_View_Helper_开头的类,类名的最后一段是助手的名字,助手的名字必须是首字母大写的,该类必须至少有一个以助手名字命名的方法。助手名通常是驼峰式命名,即它不会是大写字母开头的。类名是混合大小写字格式。方法名也是驼峰式命名。
默认的助手的路径通常指向Zend/View/Helper。即使用setHelperPath()方法重新指定了路径,该路径也会保持以使默认的助手能够工作。
6.9.1ZF自带的助手
示例代码:
echo $this->form(’frm1′, array(’action’=>’action.php’, ‘method’=>’post’), false) .”\n”;
echo $this->formHidden(’id’, ’submited’);
$content = ‘Your Name:’ . $this->formText(’name’, ”, array(’size’ => 20)) .’
’;
$content .= ‘Password:’ . $this->formPassword(’pass’, ”, array(’size’ => 20));
echo $this->fieldset(’flst’, $content, array(’legend’=>’Name:’, ’style’=>’width:200pt’)) .’
’;
echo $this->formLabel(’email’, ‘Your Email:’);
echo $this->formText(’email’, ‘you@example.com’, array(’size’ => 32)) .’
’;
echo ‘Your Country:’;
echo $this->formSelect(’country’, ‘us’, null, $this->countries) .’
’;
echo ‘Would you like to opt in?’;
echo $this->formCheckbox(’opt_in’, ‘yes’, null, array(’yes’, ‘no’)) .’
’;
echo ‘Choose them:’;
echo $this->formMultiCheckbox(’chkbox’, ‘A’, null, array(’A'=>’valA’,'B’=>’valB’,'C’=>’valC’,'D’=>’valD’), ‘
’) .’
’;
echo ‘Choose one:’;
echo $this->formRadio(’radio’, ‘A’, null, array(’A'=>’valA’,'B’=>’valB’,'C’=>’valC’,'D’=>’valD’), ”) .’
’;
echo $this->htmlList($this->countries) .’
’;
echo $this->url(array(’k1′=>’v1′,’k2′=>’v2′,’k3′=>’v3′)) .’
’;
echo $this->formTextarea(’ta’, ”, array(’rows’=>’5′,’cols’=>’25′)) .’
’;
echo $this->formFile(’file’, ”, array()) .’
’;
echo $this->formButton(’btn’, ‘BUTTON’, array(’onClick’=>”));
echo $this->formSubmit(’OK’, ‘OK’);
echo $this->formReset(’reset’, ‘Reset’);
?>
6.9.2动作(Action)助手
允许我们在视图脚本里执行某个控制器里的动作方法。
示例:
6.9.3区域(Partial)助手
区域助手的基本用法是在它自己的视图范围内解析另一个模板的片段,类似视图脚本嵌套调用。
区域助手的基本用法:
示例:
booklist.php文件包含脚本片段:
if ($this->books):
?>
作者 | 书名 |
---|---|
escape($val['author']) ?> | escape($val['title']) ?> |
else:
?>
There are no books to display.
endif;
?>
在另一个视图脚本通过Partial助手调用booklist.php文件:
= $this->partial(’booklist.php’, array(
‘books’ => array(
array(’author’=>’zhangqing’,'title’=>’《book for php》’),
array(’author’=>’zhangking’,'title’=>’《book for jsp》’),
array(’author’=>’zhanghing’,'title’=>’《book for asp.net》’),
)));
?>
’; ?>
$model = array(
array(’key’ => ‘Mammal’, ‘value’ => ‘Camel’),
array(’key’ => ‘Bird’, ‘value’ => ‘Penguin’),
array(’key’ => ‘Reptile’, ‘value’ => ‘Asp’),
array(’key’ => ‘Fish’, ‘value’ => ‘Flounder’),
);
?>
= $this->partialLoop(’partialLoop.phtml’, $model) ?>
使用 PartialLoop 来解析可迭代的(Iterable)的模型
示例:
$model = array(
array(’key’ => ‘Mammal’, ‘value’ => ‘Camel’),
array(’key’ => ‘Bird’, ‘value’ => ‘Penguin’),
array(’key’ => ‘Reptile’, ‘value’ => ‘Asp’),
array(’key’ => ‘Fish’, ‘value’ => ‘Flounder’),
);
?>
= $this->partialLoop(’partialLoop.phtml’, $model) ?>
以上代码中partialLoop 调用了以下partialLoop.phtml的内容:
6.9.4占位(Placeholder)助手
示例:
= $this->doctype(’XHTML1_STRICT’) ?>
// setting meta keywords
$this->headMeta()->appendName(’keywords’, ‘framework php productivity’);
$this->headMeta()->appendHttpEquiv(’expires’, ‘Wed, 26 Feb 1997 08:21:57 GMT’)
->appendHttpEquiv(’pragma’, ‘no-cache’)
->appendHttpEquiv(’Cache-Control’, ‘no-cache’);
$this->headMeta()->appendHttpEquiv(’Content-Type’, ‘text/html; charset=UTF-8′)
->appendHttpEquiv(’Content-Language’, ‘en-US’);
$this->headMeta()->appendHttpEquiv(’Refresh’, ‘3;URL=http://www.some.org/some.html’);
echo $this->headMeta();
$this->headScript()->appendFile(’/js/prototype.js’)
->appendScript(’xx.js’);
// Putting scripts in order
// place at a particular offset to ensure loaded last
$this->headScript()->offsetSetScript(100, ‘/js/myfuncs.js’);
// use scriptaculous effects (append uses next index, 101)
$this->headScript()->appendScript(’/js/scriptaculous.js’);
// but always have base prototype script load first:
$this->headScript()->prependScript(’/js/prototype.js’);
echo $this->headScript();
// setting links in a view script:
$this->headLink()->appendStylesheet(’/styles/basic.CSS’)
->headLink(array(’rel’ => ‘favicon’, ‘href’ => ‘/img/favicon.ico’), ‘PREPEND’)
->prependStylesheet(’/styles/moz.css’, ’screen’, true);
// rendering the links:
echo $this->headLink();
?>
$this->placeholder(’foo’)->setPrefix(”
->setSeparator(”
->setIndent(4)
->setPostfix(”
$this->placeholder(’foo’)->set(”Some text for later-1″);
$this->placeholder(’foo’)->bar = “Some text for later-2″;
echo $this->placeholder(’foo’);
$foo = $this->placeholder(’foo’);
echo $foo[0] .’
’;
echo $foo['bar'] .’
’;
?>
$this->placeholder(’hoo’)->captureStart();
foreach ($this->data as $datum)
{
?>
= $datum['content'] ?>
}
$this->placeholder(’hoo’)->captureEnd();
?>
echo $this->placeholder(’hoo’)
?>
$this->placeholder(’woo’)->captureStart(’SET’, ‘data’);
foreach ($this->data as $datum):
?>
= $datum['content'] ?>
endforeach;
$this->placeholder(’woo’)->captureEnd();
?>
echo $this->placeholder(’woo’)->data;
?>
6.9.4自定义助手
编写及调用自定义助手的方法:
The class name must, at the very minimum, end with the helper name itself, using MixedCaps. E.g., if you were writing a helper called “specialPurpose”, the class name would minimally need to be “SpecialPurpose”. You may, and should, give the class name a prefix, and it is recommended that you use ‘View_Helper’ as part of that prefix: “My_View_Helper_SpecialPurpose”. (You will need to pass in the prefix, with or without the trailing underscore, to addHelperPath() or setHelperPath()).
The class must have a public method that matches the helper name; this is the method that will be called when your template calls “$this->specialPurpose()”. In our “specialPurpose” helper example, the required method declaration would be “public function specialPurpose()”.
In general, the class should not echo or print or otherwise generate output. Instead, it should return values to be printed or echoed. The returned values should be escaped appropriately.
The class must be in a file named after the helper class. Again using our “specialPurpose” helper example, the file has to be named “SpecialPurpose.php”.
Place the helper class file somewhere in your helper path stack, and Zend_View will automatically load, instantiate, persist, and execute it for you.
示例:
//Custom Helper “myFieldset”
echo $this->myFieldset(’This my custom Helper.’).’
’;
echo $this->myFieldset(’This my custom Helper.’).’
’;
echo $this->myFieldset(’This my custom Helper.’).’
’;
?>
调用代码:function customhelperAction()
{
$view = new Zend_View();
$view->setScriptPath(’views’);
$view->setHelperPath(’views/helpers’, ‘My_View_Helper’);
echo $view->render(’my_custom_helper.php’);
}
注意:本例只是使用Smarty引擎的其中一种方法。在ZF中还可以用别的形式来使用Smarty模板引擎,我们会在别的章节里介绍。