53.4. 视图助手(View Helper)

在你的视图脚本中,经常需要执行某些特定的复杂的功能:例如,格式化日期,生成表单对象,或者显示action的链接等等。你可以使用助手类来完成这些工作。

助手就是简单的类。假设你想要一个名为'fooBar'的助手,缺省地,类的前缀是'Zend_View_Helper_' (当设定助手路径时,你可以指定定制的前缀),类名的最后一部分就是助手名称;这一部分应该是TitleCapped(即像英文文章的标题一样,例如fooBar就要写成FooBar,by Jason Qi);所以,类的全名就是Zend_View_Helper_FooBar 。这个类应当至少有一个在助手之后命名的方法,并且是驼峰格式(即首字母小写,之后的每个单词首字母大写,例如thisIsAnExample。详见http://c2.com/cgi/wiki?CamelCase -- Haohappy注):fooBar()

[注意] 注意大小写

助手名称总是遵循驼峰格式,例如,它们从不以大写字母开头。类名是混合大小写字格式,但方法在执行时是驼峰格式。

[注意] 缺省助手路径

即使调用 setHelperPath() 来重写当前的路径,缺省助手路径总是指向 Zend Framework 视图助手, 例如:'Zend/View/Helper/',设置这个路径来确保缺省的助手工作。

在视图脚本中,你可以用$this->helperName()来调用helper。这时Zend_View会加载 Zend_View_Helper_HelperName类,建立一个对象实例,并调用它的helperName()方法。对象的实例会在 Zend_View的实例内一直存在,并可以被$this->helperName()重复调用。

53.4.1. 基本的助手

Zend_View自带了几个helper类,大部分是用来生成组件的和有自动转义变量的功能。另外,有些助手用来创建基于路由的URL和HTML列表以及声明变量。当前的助手包括:

  • declareVars(): 当使用 strictVars() 时很有用,这个助手可用来声明已经或还没有在View对象里设置的模板变量,并设置缺省值。当作参数传递改方法的数组将用来设置缺省值;否则,如果变量不存在,就设置一个空的字符串。

  • fieldset($name, $content, $attribs): 生成一个 XHTML fieldset. 如果 $attribs 包含一个 'legend' 键,它的值将用于 fieldset legend。 Fieldset 将环绕在 $content 周围提供给助手。

  • form($name, $attribs, $content): 生成一个 XHTML 表单。所有 $attribs 转义和解析成表单标签的 XHTML 属性。如果 $content 不以布尔 false 出现,那么内容就是在表单标签内被解析,如果 $content 是布尔 false (缺省),只有开头的表单标签被生成。

  • formButton($name, $value, $attribs): 生成 <button /> 元素;

  • formCheckbox($name, $value, $attribs,$options): 生成 <input type="checkbox" /> 元素。

    缺省地,当没有提供 $value 并且没有 $options,'0' 被假定为未选的值,'1'为选中的值。 如果传递 $value,但没有 $options,选中的值就被假定为传递的值。

    $options 应当是数组。如果数组被索引,第一个值就是选中的值,第二个是未选的值, 所有其它值被忽略。 你也可传递一个带有键为 'checked' 和 'unChecked' 的联合数组。

    如果传递了 $options,如果 $value 匹配选中的值,元素将标记为选中。你也通过传递 一个布尔值给属性 'checked' 来标记元素为选中或未选。

    上述内容可能最好汇总成一些例子:

    <?php
    // '1' and '0' as checked/unchecked options; not checked
    echo $this->formCheckbox('foo');
    
    // '1' and '0' as checked/unchecked options; checked
    echo $this->formCheckbox('foo', null, array('checked' => true));
    
    // 'bar' and '0' as checked/unchecked options; not checked 
    echo $this->formCheckbox('foo', 'bar');
    
    // 'bar' and '0' as checked/unchecked options; checked 
    echo $this->formCheckbox('foo', 'bar', array('checked' => true));
    
    // 'bar' and 'baz' as checked/unchecked options; unchecked 
    echo $this->formCheckbox('foo', null, null, array('bar', 'baz');
    
    // 'bar' and 'baz' as checked/unchecked options; unchecked 
    echo $this->formCheckbox('foo', null, null, array(
        'checked' => 'bar', 
        'unChecked' => 'baz'
    ));
    
    // 'bar' and 'baz' as checked/unchecked options; checked 
    echo $this->formCheckbox('foo', 'bar', null, array('bar', 'baz');
    echo $this->formCheckbox('foo', null, array('checked' => true), array('bar', 'baz');
    
    // 'bar' and 'baz' as checked/unchecked options; unchecked 
    echo $this->formCheckbox('foo', 'baz', null, array('bar', 'baz');
    echo $this->formCheckbox('foo', null, array('checked' => false), array('bar', 'baz');
    

    对所有情况,标记预先准备一个带有未选的值的隐藏元素。 这样,如果值是未选的,你将仍获得有效的返回到表单的值。

  • formErrors($errors, $options): 生成一个无顺序的 XHTML 列表来显示错误。 $errors 是个字符串或字符串数组;$options 是你想放入开头列表标签的任何属性。

    当通过调用助手中的方法时,你可以指定替代的开头,结尾和分隔符:

    • setElementStart($string); 缺省为 '<ul class="errors"%s"><li>', 其中 %s 是在 $options 中被替换的属性。

    • setElementSeparator($string); 缺省为 '</li><li>'。

    • setElementEnd($string); 缺省为 '</li></ul>'。

  • formFile($name, $value, $attribs): 生成<input type="file" />

  • formHidden($name, $value, $attribs): 生成<input type="hidden" />

  • formLabel($name, $value, $attribs): 生成 <label>设置 for 属性给 $name,实际的标签字符给 $value。 如果 disable 传递给 attribs,什么都不返回。

  • formMultiCheckbox($name, $value, $attribs, $options, $listsep): 生成一个 checkboxes 列表。$options 是个联合数组,可以有任意的深度。$value 可以是单个的值或者是可选的匹配在 $options 数组中的键的一个数组。$listsep 缺省为一个 HTML break ("<br />")。缺省地,这个元素被当作数组,所有 checkboxes 共享同一个名称,并以数组的形式提交。

  • formPassword($name, $value, $attribs): Creates an <input type="password" /> element.

  • formRadio($name, $value, $attribs, $options): 生成一系列<input type="button" />,每个$options数组元素一个,key为radio的值,并且元素的值是radio的标签。

  • formReset($name, $value, $attribs): 生成<input type="reset" />

  • formSelect($name, $value, $attribs, $options): 生成<select>...</select>其中的每个<option>对应于一个$option数组元素。元素的key是option的值,元素的值是option的标签。$value这个值的option默认为选中。

  • formSubmit($name, $value, $attribs): 生成<input type="submit" />

  • formText($name, $value, $attribs): 生成<input type="text" />

  • formTextarea($name, $value, $attribs): 生成<textarea>...</textarea>

  • url($urlOptions, $name, $reset): 基于已命名的路由生成URL字符串。$urlOptions 必须是一个键/值对应的关联数组,用于特定的路由。

  • htmlList($items, $ordered, $attribs): 基于传递给它的 $items 生成无序的和有序的列表。如果 $items 是多维数组,将创建嵌套的列表。

以上helper的使用非常简单,下面是个例子。注意你只需要调用即可,它们会根据需要自己加载并实例化。

<?php
// 在你的view脚本内部, $this 指向 Zend_View 实例.
//
// 假设你已经为select对应的变量$countries指定一系列option值,
// array('us' => 'United States', 'il' =>
// 'Israel', 'de' => 'Germany').
?>
<form action="action.php" method="post">
    <p><label>Your Email:
        <?php echo $this->formText('email', '[email protected]', array('size' => 32)) ?>
    </label></p>
    <p><label>Your Country:
        <?php echo $this->formSelect('country', 'us', null, $this->countries) ?>
    </label></p>
    <p><label>Would you like to opt in?
        <?php echo $this->formCheckbox('opt_in', 'yes', null, array('yes', 'no')) ?>
    </label></p>
</form>
        

以上视图脚本会输出这样的结果:

<form action="action.php" method="post">
    <p><label>Your Email:
        <input type="text" name="email" value="[email protected]" size="32" />
    </label></p>
    <p><label>Your Country:
        <select name="country">
            <option value="us" selected="selected">United States</option>
            <option value="il">Israel</option>
            <option value="de">Germany</option>
        </select>
    </label></p>
    <p><label>Would you like to opt in?
        <input type="hidden" name="opt_in" value="no" />
        <input type="checkbox" name="opt_in" value="yes" checked="checked" />
    </label></p>
</form>
        

53.4.1.1. 动作视图助手

Action 视图助手允许视图脚本执行一个特定的控制器Action;在执行之后的响应对象的结果将被返回。有时候特定的Action生成可重用内容或“widget-ized”内容(在页面内生成一个带有特定功能的小面板,类似于Windows Vista的widget,Haohappy注),这时我们就可以使用本功能。

内部调用_forward() 或者转向的Action在此将无效,将返回空字符串。

Action视图助手的API和大部分MVC组件调用控制器动作的方式一样:action($action,$controller, $module = null, array $params = array())$action$controller 是必须的;如果没有指定模块,缺省模块将被使用。

例 53.1. 动作视图助手的基本用法

例如,假设你有一个CommentController ,为了给当前请求输出评论列表,带一个可被调用的 listAction() 方法:

<div id="sidebar right"> 
    <div class="item">
        <?= $this->action('list', 'comment', null, array('count' => 10)); ?> 
    </div>
</div>

        

53.4.1.2. 区域助手(Partial Helper)

Partial 视图助手被用来在它自己的变量范围内解析特定的模板。主要用法是(解析)可重用的模板片段,你不需要操心变量名的冲突。另外,它们让你从特定的模块指定 partial 视图脚本。

Partial 的兄弟 PartialLoop 视图助手允许传递可迭代数据并为每个条目解析一部分。

例 53.2.  Partials 的基本用法

partials 的基本用法是在它自己的视图范围内解析一个模板的片段。

<?php // partial.phtml ?>
<ul>
    <li>From: <?= $this->escape($this->from) ?></li>
    <li>Subject: <?= $this->escape($this->subject) ?></li>
</ul>

你可以这样从视图脚本里调用它:

<?= $this->partial('partial.phtml', array(
    'from' => 'Team Framework', 
    'subject' => 'view partials')); ?>

解析结果如下:

<ul>
    <li>From: Team Framework</li>
    <li>Subject: view partials</li>
</ul>

[注意] 什么是模型(model)?

Partial 视图助手一起使用的模型(即partial()的第二个参数,Haohappy注)可以是下列其中之一:

  • 数组 。如果传递了数组,它应当是联合数组,因为它的 ‘键/值’ 对用作为视图变量的键赋值给视图。

  • 实现了toArray() 方法的对象 。如果被传递的对象有 toArray() 方法,toArray()的结果将被当作视图变量赋值给视图对象。

  • 标准对象 。 任何其它对象将把object_get_vars()的结果(对象的所有公共属性)赋值给视图对象。

如果你的模型是一个对象,你可能想让它作为对象传递给 partial 脚本,而不是把它系列化成一个数组变量。 你可以通过设置适当的助手的 'objectKey' 属性来完成这个:

// Tell partial to pass objects as 'model' variable
$view->partial()->setObjectKey('model');

// Tell partial to pass objects from partialLoop as 'model' variable in final
// partial view script:
$view->partialLoop()->setObjectKey('model');

当传递 Zend_Db_Table_Rowsets 给 partialLoop()时这个技术相当有用, 因为你在视图脚本里有全部访问 row 对象的权限,允许你调用它们的方法(如从父或依赖的 rows 获取数据)。

例 53.3.  使用 PartialLoop 来解析可迭代的(Iterable)的模型

可能你常常会需要在一个循环里使用 partials 来输出相同的内容片段多次,这时你就可以把大块的重复的内容或复杂的显示逻辑放到一个地方。然而这对性能有影响,因为partial助手需要在每个迭代里调用一次。

PartialLoop 视图助手解决了这个问题。 它允许你把迭代条目(实现Iterator的数组或对象)当做模型来传递。它这些把这些条目当作模型迭代、传递给 partial 脚本。在迭代器里的条目可以是 Partial 视图助手允许的任何模型。

让我们看一下下面的 partial 视图脚本:

<? // partialLoop.phtml ?>
    <dt><?= $this->key ?></dt>
    <dd><?= $this->value ?></dd>

添加下列 "model":

<?php
$model = array(
    array('key' => 'Mammal', 'value' => 'Camel'),
    array('key' => 'Bird', 'value' => 'Penguin'),
    array('key' => 'Reptile', 'value' => 'Asp'),
    array('key' => 'Fish', 'value' => 'Flounder'),
);
?>

在视图脚本中,你可以这样调用 PartialLoop 助手:

<dl>
<?= $this->partialLoop('partialLoop.phtml', $model) ?>
</dl>
<dl></dl>
    <dt>Mammal</dt>
    <dd>Camel</dd>

    <dt>Bird</dt>
    <dd>Penguin</dd>

    <dt>Reptile</dt>
    <dd>Asp</dd>

    <dt>Fish</dt>
    <dd>Flounder</dd>

</dl>

例 53.4.  在其它模块中解析 Partials

有时候 partial 存在于不同的模块(Module)。如果你知道模块的名称,你可以把它当作第二个参数传递给 partial() 或者 partialLoop(),把 $model 作为第三个参数。

例如,如果一个你想用一个在 'list' 模块的 pager partial,就象下面这样来运用:

<?= $this->partial('pager.phtml', 'list', $pagerData) ?>

这样,你可以重用原来是特别供给其它模块使用的 partials 。所以,在共享的视图脚本路径里放置可重用的 partials 很可能是个好习惯。


53.4.1.3.  占位符助手(Placeholder Helper)

Placeholder 视图助手用来在视图脚本和视图实例之间持久化内容。它也提供一些有用的功能如:聚合内容、抓取视图脚本内容以后来用和添加前置(pre-)和后置(post-)文本到内容 (还可以为聚合内容定制分隔符等)。

例 53.5.  占位符的基本用法

占位符的基本用法是将视图中的数据持久化。每个Placeholder助手的启用都需要一个占位符名称,助手接着返回一个占位符容器对象,你可以用来处理或者简单地输出。

<?php $this->placeholder('foo')->set("Some text for later") ?>

<?php 
    echo $this->placeholder('foo'); 
    // outputs "Some text for later"
?>

例 53.6.  用占位符来聚合内容

通过占位符来聚合内容有时候很有用。例如视图脚本可拥有一个变量数组来存放数据,而视图脚本可以决定这些数据如何显示出来。

Placeholder 视图助手使用继承自 ArrayObject 的容器,为处理数组提供了丰富的功能。另外,它为格式化存储在容器里的内容提供了众多的方法:

  • setPrefix($prefix) 用内容的前缀设置文本。任何时候使用 getPrefix() 来确定当前的设置是什么。

  • setPostfix($prefix) 用要追加的内容设置文本。任何时候使用 getPostfix() 来确定当前的设置是什么。

  • setSeparator($prefix) 设置用来分隔聚合内容的分隔符。任何时候使用 getSeparator() 来确定当前的设置是什么。

  • setIndent($prefix) 可以用来给内容设置一个缩进的值。如果传递一个整数,就按这个数量来缩进;如果传递一个字符串,就按字符串的长度来缩进。任何时候使用 getIndent() 来确定当前的设置是什么。

<!-- first view script -->
<?php $this->placeholder('foo')->exchangeArray($this->data) ?>
<!-- later view script -->
<?php 
$this->placeholder('foo')->setPrefix("<ul>\n    <li>")
                         ->setSeparator("</li><li>\n") 
                         ->setIndent(4)
                         ->setPostfix("</li></ul>\n");
?>

<?php 
    echo $this->placeholder('foo'); 
	//输出一个带有漂亮缩进的HTML无序列表
?>

因为 Placeholder 容器对象从 ArrayObject 继承而来,所以你可以很容易地给特定的键赋值,而不是简单地把它压进容器。键可以作为对象属性或数组键来访问。

<?php $this->placeholder('foo')->bar = $this->data ?>
<?php echo $this->placeholder('foo')->bar ?>

<?php
$foo = $this->placeholder('foo');
echo $foo['bar'];
?>

例 53.7.  使用占位符(Placeholders)来抓取内容

有时你可能会在视图脚本(最容易的模板)为占位符放些内容,Placeholder 视图助手允许你抓取任意的内容,在以后用下列的 API 来解析并输出(本功能类似于缓冲输出--Haohappy注):

  • captureStart($type, $key) 开始抓取内容。

    $type 应该是 Placeholder 的常量 APPENDSET 其中之一。如果用 APPEND,被抓取得内容就追加到在占位符当前内容的列表; 如果用 SET,被抓取得内容就被用作占位符的唯一内容(替换以前的内容)。缺省地 $typeAPPEND

    $key可用来在占位符容器指定一个特殊的键给你想抓取的内容。

    captureStart() 锁住抓取直到 captureEnd() 被调用,不能在同一个占位符容器里嵌套抓取,这样做会引起一个异常。

  • captureEnd() 使抓取内容停止,并根据 captureStart() 如何被调用来把它放到容器对象。

<!-- Default capture: append -->
<?php $this->placeholder('foo')->captureStart(); 
foreach ($this->data as $datum): ?>
<div class="foo">
    <h2><?= $datum->title ?></h2>
    <p><?= $datum->content ?></p>
</div> 
 <?php endforeach; ?>
<?php $this->placeholder('foo')->captureEnd() ?>

<?php echo $this->placeholder('foo') ?>
<!-- Capture to key -->
<?php $this->placeholder('foo')->captureStart('SET', 'data');
foreach ($this->data as $datum): ?>
<div class="foo">
    <h2><?= $datum->title ?></h2>
    <p><?= $datum->content ?></p>
</div> 
 <?php endforeach; ?>
<?php $this->placeholder('foo')->captureEnd() ?>

<?php echo $this->placeholder('foo')->data ?>

53.4.1.3.1.  具体占位符实现

Zend Framework 自带有若干个具体实现的占位符,包括常用的占位符:doctype、page title、以及各种 <head> 元素。对所有情况,不带参数调用占位符将返回元素自己。

每个元素的文档,请查看如下链接:

53.4.1.4. 文档类型助手(Doctype Helper)

有效的HTML和XHTML文档应当包括一个 DOCTYPE 声明。但是文档类型声明很难记忆,而且会影响到文档中的特定元素的解析(例如,在 <script><style> 元素中转义的CDATA)。

Doctype 助手允许指定下列类型之一:

  • XHTML11

  • XHTML1_STRICT

  • XHTML1_TRANSITIONAL

  • XHTML1_FRAMESET

  • XHTML_BASIC1

  • HTML4_STRICT

  • HTML4_LOOSE

  • HTML4_FRAMESET

你也可以指定一个自己定制的带有良好结构的文档类型。

Doctype助手是 占位符助手 的一个具体的实现。

例 53.8. Doctype 助手的基本用法

在任何时候都可以指定 doctype。然而,依赖 doctype 输出的助手只在你设置后才认识它,所以最简单的用法是在 bootstrap 脚本中指定:

$doctypeHelper = new Zend_View_Helper_Doctype();
$doctypeHelper->doctype('XHTML1_STRICT');

然后在布局脚本中输出:

<?php echo $this->doctype() ?>

例 53.9. 获取 Doctype

如果需要知道文档类型,可以在由调用助手返回的对象中调用getDoctype()

<?php
$doctype = $view->doctype()->getDoctype();
?>

很常见地,你需要知道doctype是否XHTML;那么,isXhtml()方法已经足够:

<?php
if ($view->doctype()->isXhtml()) {
    // do something differently
}
?>

53.4.1.5. HeadLink 助手

HTML的 <link> 标签越来越多地用于为你的站点链接不同资源:stylesheet, feed, favicon, trackback等。HeadLink 助手提供了一个简单的接口用来创建和聚合这些元素以供稍后在你的布局脚本里获取和输出。

HeadLink 助手有以下特殊的方法用来添加 stylesheet 链接:

  • appendStylesheet($href, $media, $conditionalStylesheet)

  • offsetSetStylesheet($index, $href, $media, $conditionalStylesheet)

  • prependStylesheet($href, $media, $conditionalStylesheet)

  • setStylesheet($href, $media, $conditionalStylesheet)

$media 值缺省为 'screen',但可以为任何有效的媒体(media)值。$conditionalStylesheet 是布尔值,将用于解析时来决定是否有特殊的注释应该被包括以防止在特定的平台上加载stylesheet。

另外,HeadLink 助手有特殊的方法用来添加 'alternate' 链接到它的堆栈:

  • appendAlternate($href, $type, $title)

  • offsetSetAlternate($index, $href, $type, $title)

  • prependAlternate($href, $type, $title)

  • setAlternate($href, $type, $title)

headLink() 助手方法允许指定所有的必要的属性给 <link> 元素, 也允许指定替代 - 是否新元素替换所有其他的,前置(栈顶),或追加(栈底)。

HeadLink 助手是 占位符助手 的一个具体实现。

例 53.10. HeadLink 助手的基本用法

任何时候可以指定headLink,典型地,将在布局脚本里指定全局链接,并在应用程序视图脚本里指定特定的链接。在布局脚本里的 <head> 部份,用echo来输出。

<?php // 在视图脚本中设置链接:
$this->headLink()->appendStylesheet('/styles/basic.css')
                 ->headLink(array('rel' => 'favicon', 'href' => '/img/favicon.ico'), 'PREPEND')
                 ->prependStylesheet('/styles/moz.css', 'screen', true);
?>
<?php // 解析链接:?>
<?= $this->headLink() ?>

53.4.1.6. HeadMeta 助手

HTML <meta>元素用来提供关于HTML文档的 meta 信息-如关键字,文档字符集,缓冲的 pragama 等。Meta标签可以是'http-equiv' 或 'name' 类型,必须包含'content'属性,并且也可以有 'lang' 或 'scheme' 修饰属性。

HeadMeta 助手提供下列方法来设置和添加 meta 标签:

  • appendName($keyValue, $content, $conditionalName)

  • offsetSetName($index, $keyValue, $content, $conditionalName)

  • prependName($keyValue, $content, $conditionalName)

  • setName($keyValue, $content, $modifiers)

  • appendHttpEquiv($keyValue, $content, $conditionalHttpEquiv)

  • offsetSetHttpEquiv($index, $keyValue, $content, $conditionalHttpEquiv)

  • prependHttpEquiv($keyValue, $content, $conditionalHttpEquiv)

  • setHttpEquiv($keyValue, $content, $modifiers)

$keyValue 参数用来定义'name'的值或'http-equive'键;$content 是'content' 键的值,$modifiers 是可选的包含'lang' 和/或 'scheme'键的联合数组。

也可以用 headMeta() 助手方法来设置 meta 标签:headMeta($content, $keyValue, $keyType = 'name', $modifiers = array(), $placement = 'APPEND')$keyValue 是指定在 $keyType 里的键的内容,$keyType 应该是'name' 或 'http-equiv'。$placement 可以是 'SET' (覆盖所有先前存储的值), 'APPEND' (添加到栈尾)或 'PREPEND'(添加到栈顶)。

HeadMeta 覆盖每个 append()offsetSet()prepend()set() 来加强上面列出的特殊方法的用法。在内部,它存储每个条目为 stdClass 令牌,它稍后用 itemToString() 方法来序列化。这允许在堆栈的条目中执行检查,并可选地通过修改对象返回来修改这些条目。

HeadMeta助手是 占位符助手 的一个具体实现。

例 53.11. HeadMeta 助手基本用法

你可以在任何时候指定一个新的meta标签。例如指定客户端缓冲规则或SEO关键字。

例如,如果想指定SEO关键字,要创建带有名为'keywords'和内容(在页面上和关键字有关联的)的meta名称标签:

<?php // setting meta keywords
$this->headMeta()->appendName('keywords', 'framework php productivity');
?>

如果想设置一些客户端缓冲规则,最好设置带有想执行的规则的 http-equiv 标签:

<?php // 禁止客户端缓存
$this->headMeta()->appendHttpEquiv('expires', 'Wed, 26 Feb 1997 08:21:57 GMT')
                 ->appendHttpEquiv('pragma', 'no-cache')
                 ->appendHttpEquiv('Cache-Control', 'no-cache');
?>

meta标签的另一个流行用法是设置内容类型,字符集和语言:

<?php // setting content type and character set
$this->headMeta()->appendHttpEquiv('Content-Type', 'text/html; charset=UTF-8')
                 ->appendHttpEquiv('Content-Language', 'en-US');
?>

最后一个例子,可以使用"meta refresh" 来让页面转向,一个简单的办法来显示过渡消息:

<?php // 设置以下meta可使页面3秒钟后转向一个新的url
$this->headMeta()->appendHttpEquiv('Refresh', '3;URL=http://www.some.org/some.html');
?>

在布局脚本(layout)中放置所有meta标签后,简单地echo助手,把所有内容输出:

<?= $this->headMeta() ?>

53.4.1.7. HeadScript 助手

HTML <script> 元素用来提供内嵌的客户端脚本元素或链接到远程包含客户端脚本代码的资源。你可以用HeadScript 助手来管理它们。

HeadScript 助手支持下列方法来设置和添加脚本:

  • appendFile($src, $type = 'text/javascript', $attrs = array())

  • offsetSetFile($index, $src, $type = 'text/javascript', $attrs = array())

  • prependFile($src, $type = 'text/javascript', $attrs = array())

  • setFile($src, $type = 'text/javascript', $attrs = array())

  • appendScript($script, $type = 'text/javascript', $attrs = array())

  • offsetSetScript($index, $script, $type = 'text/javascript', $attrs = array())

  • prependScript($script, $type = 'text/javascript', $attrs = array())

  • setScript($script, $type = 'text/javascript', $attrs = array())

对于所有*File()方法,$src 是要加载的脚本的远程位置,常常以 URL 或路径的形式表示。 对于所有*Script()方法,$script是客户端你想用于元素中的脚本。

HeadScript也允许抓取脚本,然后放到其它地方,如果你想通过程序生成客户端脚本,这将很有用。下面的例子里有它的用法。

最后,你也可以用 headScript()方法 来快速地添加脚本元素,它的用法是: headScript($mode = 'FILE', $spec, $placement = 'APPEND')$mode 是指链接一个文件还是一个脚本,可以是'FILE' 或 'SCRIPT' ,$spec 是链接的脚本文件或脚本代码。$placement必须为'APPEND', 'PREPEND', 或'SET'其中之一.

HeadScript 覆盖 append()offsetSet()prepend()set() 中的每一个来加强上述特殊方法的用法。在内部,它存储每个条目为 stdClass 令牌,它在稍后用 itemToString() 方法 serializes 。这允许在堆栈里检查条目,并可选地通过修改返回的对象来修改这些条目。

The HeadScript 助手是 占位符助手 的一个具体实现。

[注意] 在HTML Body中使用 InlineScript

当包含脚本内嵌在 HTML body里时,应当使用HeadScript 的兄弟助手,InlineScript。为了加速页面的加载,提高用户访问速度,特别是当使用第三方分析脚本(比如Google Analytics等流量统计系统的javascript文件 --Haohappy注),把脚本放在文档的最后是一个好的习惯。

[注意] 任意的属性缺省关闭 (Disabled)

缺省地,HeadScript 将只解析(render)由 W3C 赋予的 <script> 属性,包括 'type'、 'charset'、 'defer'、 'language' 和 'src' 。 然而,一些 javascript 框架,如 Dojo,利用定制的属性来修改行为。为了允许这样的属性,可以通过 setAllowArbitraryAttributes() 方法来打开(enable)它们:

<?php
$this->headScript()->setAllowArbitraryAttributes(true);
?>

例 53.12. HeadScript 助手基本用法

在任何时候可以指定一个新的脚本。如上所述,可以链接到外部资源文件或脚本自己。

<?php // adding scripts
$this->headScript()->appendFile('/js/prototype.js')
                   ->appendScript($onloadScript);
?>

在客户端脚本编程中,顺序常常很重要,因为依赖的缘故,需要确保按特定的顺序来加载库,使用 append、 prepend 和 offsetSet 指令来帮助完成任务:

<?php // 按顺序放置脚本文件

//设置偏移量来确保这个文件最后加载
$this->headScript()->offsetSetScript(100, '/js/myfuncs.js');

//使用scriptaculous效果文件,这时append动作使用索引101,接上行代码的索引
$this->headScript()->appendScript('/js/scriptaculous.js');

//但总是保证prototype文件首先加载
$this->headScript()->prependScript('/js/prototype.js');
?>

当准备好输出所有脚本到布局脚本,简单地 echo 这个助手:

<?= $this->headScript() ?>

例 53.13. Capturing Scripts Using the HeadScript Helper

有时候,需要“编程式”地生成客户端脚本。你可以使用字符串串联、heredoc或类似的技术(字符串串联即$string1.$string2这种形式,heredoc即使用<<<操作符--Haohappy注),通常通过创建脚本和在PHP标签里做手脚会更容易些。HeadScript 可以实现这个功能,把一段JavaScript代码抓取到堆栈中暂存(道理同缓冲输出--Haohappy注):

<?php $this->headScript()->captureStart() ?>
var action = '<?= $this->baseUrl ?>';
$('foo_form').action = action;
<?php $this->headScript()->captureEnd() ?>

下面是上例中的一些假设:

  • 脚本将追加到堆栈。如果需要替换或者追加到堆栈顶部,那么需要分别把 'SET' 或 'PREPEND' 作为第一个参数传递给 captureStart()

  • 脚本 MIME 类型假定为 'text/javascript',如果想指定一个不同的类型,需要把它作为第二个参数传递给 captureStart()

  • 如果需要为 <script> 标签指定附加属性, 把它们放入数组作为第三个参数传递给 captureStart()


53.4.1.8. HeadStyle 助手

在 HTML <head> 元素中 HTML <style> 元素用来包含 CSS stylesheets inline 。

[注意] 使用 HeadLink 来链接 CSS 文件

HeadLink 为包含外部 stylesheets 应该用来生成 <link> 元素。如果想定义自己的 stylesheets inline, 使用 HeadScript

HeadStyle 助手支持下列方法来设置和添加 stylesheet 声明:

  • appendStyle($content, $attributes = array())

  • offsetSetStyle($index, $content, $attributes = array())

  • prependStyle($content, $attributes = array())

  • setStyle($content, $attributes = array())

对于所有情况,$content 是实际上的 CSS 声明。$attributes 是提供给 style 标签的任何额外的属性:lang、 title、 media 或 dir 都是允许的。

HeadStyle 也允许抓取样式声明,如果想用程序生成声明,然后在任何地方自由使用,这很有用。这个用法将在下面的例子给出。

最后,你也可以用 headStyle()方法 来快速地添加声明元素,它的用法: headStyle($content$placement = 'APPEND', $attributes = array())$placement 是 'APPEND'、 'PREPEND' 或 'SET'。

HeadStyle 覆盖 append()offsetSet()prepend()set() 中的每一个来加强上述特殊方法的用法。 在内部,它存储每个条目为 stdClass 令牌,它在稍后用 itemToString() 方法 serializes 。这允许在堆栈里检查条目,并可选地通过修改返回的对象来修改这些条目。

HeadStyle 助手是 占位符助手 的一个具体实现。

例 53.14. HeadStyle 助手的基本用法

在任何时候都可以指定一个新的样式标签:

<?php // adding styles
$this->headStyle()->appendStyle($styles);
?>

对 CSS 来说,顺序非常重要,因为层叠的顺序(the order of the cascade),你需要确保样式表中的声明按特定的顺序加载。使用append、 prepend 和 offsetSet 指令可帮助你达到目的:

<?php // Putting styles in order

// place at a particular offset:
$this->headStyle()->offsetSetStyle(100, $customStyles);

// place at end:
$this->headStyle()->appendStyle($finalStyles);

// place at beginning
$this->headStyle()->prependStyle($firstStyles);
?>

当准备好在布局脚本里输出所有样式声明,简单地 echo 助手:

<?= $this->headStyle() ?>

例 53.15.  用 HeadStyle 助手抓取样式声明

有时候需要用程序生成 CSS 样式声明。当你可以使用字符串串联,heredoc等等, 通常通过创建脚本和在PHP标签里做手脚会更容易些。HeadStyle 可以做这个,把它抓取到堆栈:

<?php $this->headStyle()->captureStart() ?>
body {
    background-color: <?= $this->bgColor ?>;
}
<?php $this->headStyle()->captureEnd() ?>

下面是一些假设:

  • 样式声明将追加到堆栈。如果需要替换或者加到堆栈顶部,那么需要分别把 'SET' 或 'PREPEND' 作为第一个参数传递给 captureStart()

  • 如果想指定任何另外的属性给 <style> 标签,通过数组把它们作为第二个参数传递给 captureStart()


53.4.1.9. HeadTitle 助手

HTML <title> 元素用来提供标题给HTML文档。HeadTitle 助手允许用程序生成和存储标题供以后解析和输出。

HeadTitle 助手是 占位符助手 的一个具体实现。 它覆盖 toString() 方法来确保生成 <title> 元素,并添加一个 headTitle() 方法来快速并容易地设置和标题元素的聚合。那个方法的 signature 是 headTitle($title, $setType = 'APPEND'),缺省地是追加到堆栈(聚合标题元素)的值,但你也可以指定 'PREPEND' ( 放栈顶 ) 或 'SET' ( 重写堆栈 )。

例 53.16. HeadTitle 助手基本用法

你可以在任何时候指定一个标题标签。一般的用法可以让你在应用程序的每一个层次来设置标题段:站点、控制器、动作和潜在的资源。

<?php 
// setting the controller and action name as title segments:
//把控制器和动作的名称设置为标题的一部分
$request = Zend_Controller_Front::getInstance()->getRequest();
$this->headTitle($request->getActionName())
     ->headTitle($request->getControllerName());

// setting the site in the title; possibly in the layout script:
//添加标题内容,这种写法常用于布局脚本中
$this->headTitle('Zend Framework');

// setting a separator string for segments:
//为标题的各部分设置分隔符
$this->headTitle()->setSeparator(' / ');
?>

在布局脚本中准备好标题后,使用echo即可输出:

<!-- renders <action> / <controller> / Zend Framework -->
<?= $this->headTitle() ?>

53.4.1.10. HTML 对象助手

HTML <object> 元素在网页里用于嵌入媒体如 Flash 或 QuickTime。 对象视图助手用最小的代价来帮助嵌入媒体。

有四个初始的对象助手:

  • formFlash 为嵌入 Flash 文件生成 markup。

  • formObject 为嵌入定制对象生成 markup。

  • formPage 为嵌入其它 (X)HTML 页面生成 markup。

  • formQuicktime 为嵌入 QuickTime 文件生成 markup。

所有这些助手使用相似的接口。这样,本文档只包含两个助手的例子。

例 53.17. Flash 助手

使用助手嵌入 Flash 到你的页面相当简单。唯一需要的参数是资源 URI。

<?php echo $this->htmlFlash('/path/to/flash.swf'); ?>

它输出下列 HTML:

<object data="/path/to/flash.swf" type="application/x-shockwave-flash" 
    classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" 
    codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
</object>

另外你可以指定可以和 <object> 一起解析的属性、参数和内容。 这个用 htmlObject 助手来示范。

例 53.18.  通过传递另外的参数来定制对象

对象助手里的第一个参数总是必需的,它是你想嵌入的资源的 URI。 第二个参数只对 htmlObject 助手是必需的,其它助手对这个参数已经有了正确的值。 第三个参数用来传递属性到对象元素,它只接受带有key-value对的数组,classidcodebase 是这个属性的例子。 第四个参数也用带有key-value对的数组并用它们生成 <param> 元素,你将很快看到一个这样的例子。 最后一个是提供另外的内容给对象的选项。来看一下使用所有参数的例子。

echo $this->htmlObject(
    '/path/to/file.ext', 
    'mime/type', 
    array(
        'attr1' => 'aval1', 
        'attr2' => 'aval2'
    ), 
    array(
        'param1' => 'pval1', 
        'param2' => 'pval2'
    ), 
    'some content'
);

/*
这将输出:

<object data="/path/to/file.ext" type="mime/type" 
    attr1="aval1" attr2="aval2">
    <param name="param1" value="pval1" />
    <param name="param2" value="pval2" />
    some content
</object>
*/

53.4.1.11. InlineScript 助手

HTML <script> 标签用来提供内嵌的客户端脚本,或者链接到远程包含客户端脚本代码的资源。InlineScript 助手允许你管理这两者。它由HeadScript派生,并且HeadScript助手的任何方法都可用,唯一不同的是需要用 inlineScript() 方法代替 headScript()

[注意] 在 HTML Body 里脚本使用 InlineScript

如果想在HTML body里内嵌脚本,应当使用 InlineScript 。为了加快页面加载速度(特别是使用第三方分析脚本时),把脚本放到文档的尾部是个很好的习惯。

一些 JS 库必须包含在 HTML head部分,对于这些类库请使用 HeadScript

53.4.1.12.  JSON 助手

当创建返回 JSON 的视图,设置适当的响应头也非常重要,JSON 视图助手就是来做这个的。另外,缺省地,它关闭(disable)了布局(如果布局是打开(enable)的),因为布局一般不和 JSON 响应一起使用。

JSON 助手设置下列的头:

Content-Type: application/json

当解析响应来决定如何处理内容,大部分 AJAX 库寻找这个头。

JSON 助手的用法相当简单:

<?php
<?= $this->json($this->data) ?>
?>

53.4.1.13.  翻译助手

通常,网站支持多种语言。使用 Zend 翻译 来翻译网站的内容并且 使用 翻译 视图助手来在视图里集成 Zend 翻译

在下面所有的例子里我们使用简单的数组翻译适配器(Array Translation Adapter)。当然你也可以使用任何 Zend_Translate 的实例和 Zend_Translate_Adapter 的任何子类。有若干方法来实例化 翻译 视图助手:

  • 已注册的,通过先前注册的实例

  • 后来地,通过 fluent interface (流利的接口?我还没有找到合适的词汇,by Jason Qi)

  • 直接地,通过实例化类

Zend_Translate 的已注册的实例是这个助手的首选用法。你也可以简单地在添加适配器到注册表之前选择被使用的地点(locale)

[注意] 注意

我们用地点(locales)而不用语言是因为一种语音也可以包含一个地区。例如英语有不同的方言,有英国英语和美国英语,因此我们用“地点”而不说“语言”。

例 53.19.  已注册的实例

为使用已注册的实例就是创建一个 Zend_Translate 或者 Zend_Translate_Adapter 的实例并在 Zend_Registry 里用 Zend_Translate 作为它的键。

<?php
// 我们的例子适配器
$adapter = new Zend_Translate('array', array('simple' => 'einfach'), 'de');
Zend_Registry::set('Zend_Translate', $adapter);

// 在视图中
echo $this->translate('simple');
// 返回 'einfach'
?>

如果你熟悉 fluent interface ,那么也可以在视图里创建一个实例然后实例化这个助手。

例 53.20.  在视图里

为了使用 fluent interface ,创建一个 Zend_Translate 或者 Zend_Translate_Adapter 的实例,调用不带参数的助手并调用 setTranslator 方法。

<?php
// 在视图里
$adapter = new Zend_Translate('array', array('simple' => 'einfach'), 'de');
$this->translate()->setTranslator($adapter)->translate('simple');
// 返回 'einfach'
?>

如果你使用没有Zend_View的助手,那么你也可以直接使用它。

例 53.21.  直接用法

<?php
// 我们的例子适配器
$adapter = new Zend_Translate('array', array('simple' => 'einfach'), 'de');

// 实例化适配器
$translate = new Zend_View_Helper_Translate($adapter);
print $translate->translate('simple'); // this returns 'einfach'
?>

如果你不用 Zend_View 你将需要这样做并需要创建一个已翻译的输出。


就象已经知道,translate() 方法用来返回翻译。用需要的翻译适配器的信息 id 来调用它。但它也可以在翻译字符串里替换参数。因此,它有两个方法接受变量参数。或者是参数类表,或者是参数数组。如下例:

例 53.22.  单个参数

使用单个参数就把它添加到这个方法。

<?php
// 在视图里
$date = "Monday";
$this->translate("Today is %1\$s", $date);
// 应当返回 'Heute ist Monday'
?>

[注意] 注意

记住如果使用的参数也是文本,你可能也要翻译这些参数。

例 53.23.  参数列表

使用参数列表并添加给方法。

<?php
// 在视图里
$date = "Monday";
$month = "April";
$time = "11:20:55";
$this->translate("Today is %1\$s in %2\$s. Actual time: %3\$s", $date, $month, $time);
// 应当返回 'Heute ist Monday in April. Aktuelle Zeit: 11:20:55'
?>

例 53.24.  参数数组

使用参数数组并添加到方法。

<?php
// 在视图里
$date = array("Monday", "April", "11:20:55");
$this->translate("Today is %1\$s in %2\$s. Actual time: %3\$s", $date);
// 应当返回 'Heute ist Monday in April. Aktuelle Zeit: 11:20:55'
?>

有时候必需要修改翻译的地点。可以通过动态翻译或者把所有的静态翻译来完成。并且你可以使用参数类表和参数数组。在这两种情况下,地点被当作最后一个单个参数给出。

例 53.25.  动态修改地点 (locale)

<?php
// 在视图里
$date = array("Monday", "April", "11:20:55");
$this->translate("Today is %1\$s in %2\$s. Actual time: %3\$s", $date, 'it');
?>

这个例子为 messageid 返回意大利语的翻译。但它将只能用一次。下个翻译将从适配器里设置地点。通常地在添加它到注册表之前你将在翻译适配器里设置期望的地点。但你也可以从助手里设置地点:

例 53.26.  静态修改地点 (locale)

<?php
// 在视图里
$date = array("Monday", "April", "11:20:55");
$this->translate()->setLocale('it'); 
$this->translate("Today is %1\$s in %2\$s. Actual time: %3\$s", $date);
?>

在上面的例子里设置 'it' 为新的缺省地点,它将被用来给所有将来的翻译。

当然,还有 getLocale() 方法来获得当前设置的地点。

例 53.27.  获得当前设置的地点

<?php
// 在视图里
$date = array("Monday", "April", "11:20:55");

// 从上面的例子里返回 'de' 作为缺省地点
$this->translate()->getLocale();

$this->translate()->setLocale('it'); 
$this->translate("Today is %1\$s in %2\$s. Actual time: %3\$s", $date);

// 返回 'it' 为新的缺省地点
$this->translate()->getLocale();
?>

53.4.2. 助手的路径

就像可以指定视图脚本的路径,控制器也可以为Zend_View设定助手类的路径。默认地,Zend_View会到 “Zend/View/Helper/”下查找助手类。可以用 setHelperPath()addHelperPath() 方法来告诉 Zend_View 从其它地方来找路径。另外,你也可以指定类名的前缀,用于指定助手类所在的路径,允许给助手类命名空间。默认情况下,如果没有给出前缀,会假设使用“Zend_View_Helper_”。

<?php
$view = new Zend_View();

// 设置路径为:/path/to/more/helpers, 通过使用前缀 'My_View_Helper'
$view->setHelperPath('/path/to/more/helpers', 'My_View_Helper');
        

你可以用addHelperPath()来增加helper的路径, Zend_View将使用最近增加的路径。这样你可以使用自己的helper。

<?php
$view = new Zend_View();
// Add /path/to/some/helpers with class prefix 'My_View_Helper'
$view->addHelperPath('/path/to/some/helpers', 'My_View_Helper');
// Add /other/path/to/helpers with class prefix 'Your_View_Helper'
$view->addHelperPath('/other/path/to/helpers', 'Your_View_Helper');

// now when you call $this->helperName(), Zend_View will look first for
// "/path/to/some/helpers/HelperName" using class name "Your_View_Helper_HelperName",
// then for "/other/path/to/helpers/HelperName.php" using class name "My_View_Helper_HelperName",
// and finally for "Zend/View/Helper/HelperName.php" using class name "Zend_View_Helper_HelperName".
        

53.4.3. 编写自定义的助手类

编写自定义的助手类很容易,只要遵循以下几个原则即可:

  • helper的类名最后部分必须是helper的名称,并使用混合大小写字格式。例如,你在写一个名为“specialPurpose”的类,类名将至少是"SpecialPurpose",另外你还应该给类名加上前缀,建议将“View_Helper”作为前缀的一部份:“My_View_Helper_SpecialPurpose”。(注意大小写)你将需要将前缀(包含或不包含下划线)传递给addHelperPath()setHelperPath()

  • 类中必须有一个public的方法,该方法名与helper类名相同。该方法将在你的模板调用"$this->specialPurpose()"时执行。在我们的“specialPurpose”例子中,相应的方法声明可以是“public function specialPurpose()”。

  • 一般来说,助手类不应该echo或print或有其它形式的输出。它只需要返回值就可以了。返回的数据应当被转义。

  • 类文件的命名应该是助手类的名称,比如在"specialPurpose"例子中,文件要存为“SpecialPurpose.php”。

把助手类的文件放在你的助手路径下, Zend_View就会自动加载,实例化,持久化,并执行。

下面是一个SpecialPurpose 助手代码的例子:

<?php
class My_View_Helper_SpecialPurpose 
{
    protected $_count = 0;
    public function specialPurpose()
    {
        $this->_count++;
        $output = "I have seen 'The Jerk' {$this->_count} time(s).";
        return htmlspecialchars($output);
    }
}
        

在视图代码中,可以调用 SpecialPurpose 助手任意次。它将被实例化一次,并且会在Zend_View实例的生命周期内持久存在。

<?php
// remember, in a view script, $this refers to the Zend_View instance.
echo $this->specialPurpose();
echo $this->specialPurpose();
echo $this->specialPurpose();
        

输出结果如下所示:

I have seen 'The Jerk' 1 time(s).
I have seen 'The Jerk' 2 time(s).
I have seen 'The Jerk' 3 time(s).
        

有时候需要访问调用Zend_View 对象-例如,如果需要使用已指定的编码字符集,或想解析其它视图脚本作为助手的一部分。为了访问视图对象,助手类应该有一个 setView($view) 方法,如下:

<?php
class My_View_Helper_ScriptPath
{
    public $view;

    public function setView(Zend_View_Interface $view)
    {
        $this->view = $view;
    }

    public function scriptPath($script)
    {
        return $this->view->getScriptPath($script);
    }
}
        

如果助手类有一个 setView() 方法,它将在助手类第一次实例化时被调用,并接受当前视图对象作为参数。是否让它在类里持久和如何访问,都完全取决于你。