<sect1 id="zend.view.helpers" xmlns:xi="http://www.w3.org/2001/XInclude">

    <title>视图助手（View Helper）</title>

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

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

    <note>
        <title> 注意大小写 </title>
        <para>
            助手名称总是遵循驼峰格式，例如，它们从不以大写字母开头。类名是混合大小写字格式，但方法在执行时是驼峰格式。
        </para>
    </note>
    <note>
        <title> 缺省助手路径 </title>

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



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


    <sect2 id="zend.view.helpers.initial">
        <title>基本的助手</title>

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

        <itemizedlist>

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

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

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

            <listitem><para>
                <code>formButton($name, $value, $attribs):</code> 生成 &lt;button /&gt; 元素;
            </para></listitem>

            <listitem><para>
                <code>formCheckbox($name, $value, $attribs,$options):</code> 生成 &lt;input type="checkbox" /&gt; 元素。
            </para>

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

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

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

             <para>
                  上述内容可能最好汇总成一些例子：
             </para>

                <programlisting role="php"><![CDATA[<?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');
]]></programlisting>

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

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

                <para>
                    当通过调用助手中的方法时，你可以指定替代的开头，结尾和分隔符：
                </para>

                <itemizedlist>
                    <listitem><para>
                            <code>setElementStart($string)</code>; 缺省为 '&lt;ul class="errors"%s"&gt;&lt;li&gt;', 其中 %s 是在  <code>$options</code> 中被替换的属性。
                    </para></listitem>

                    <listitem><para>
                            <code>setElementSeparator($string)</code>; 缺省为 '&lt;/li&gt;&lt;li&gt;'。
                    </para></listitem>

                    <listitem><para>
                            <code>setElementEnd($string)</code>; 缺省为 '&lt;/li&gt;&lt;/ul&gt;'。
                    </para></listitem>
                </itemizedlist>
            </listitem>

            <listitem><para>
                <code>formFile($name, $value, $attribs):</code> 生成&lt;input
                type="file" /&gt;
            </para></listitem>

            <listitem><para>
                <code>formHidden($name, $value, $attribs):</code> 生成&lt;input
                type="hidden" /&gt;
            </para></listitem>

            <listitem><para>
                <code>formLabel($name, $value, $attribs):</code> 生成
                &lt;label&gt;设置 <code>for</code> 属性给
                <code>$name</code>，实际的标签字符给 <code>$value</code>。

                如果 <code>disable</code> 传递给
                <code>attribs</code>，什么都不返回。
            </para></listitem>

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

            <listitem><para>
                <code>formPassword($name, $value, $attribs):</code> Creates an
                &lt;input type="password" /&gt; element.
            </para></listitem>

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

            <listitem><para>
                <code>formReset($name, $value, $attribs):</code> 生成&lt;input
                type="reset" /&gt;
            </para></listitem>

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

            <listitem><para>
                <code>formSubmit($name, $value, $attribs):</code> 生成&lt;input
                type="submit" /&gt;
            </para></listitem>

            <listitem><para>
                <code>formText($name, $value, $attribs):</code> 生成&lt;input
                type="text" /&gt;
            </para></listitem>

            <listitem><para>
                <code>formTextarea($name, $value, $attribs):</code> 生成&lt;textarea&gt;...&lt;/textarea&gt;
            </para></listitem>

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

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

        </itemizedlist>

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

        <programlisting role="php"><![CDATA[<?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', 'you@example.com', 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>]]>
        </programlisting>

        <para>
            以上视图脚本会输出这样的结果：
        </para>

        <programlisting role="php"><![CDATA[<form action="action.php" method="post">
    <p><label>Your Email:
        <input type="text" name="email" value="you@example.com" 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>]]>
        </programlisting>

        <xi:include href="Zend_View-Helpers-Action.xml" />
        <xi:include href="Zend_View-Helpers-Partial.xml" />
        <xi:include href="Zend_View-Helpers-Placeholder.xml" />
        <xi:include href="Zend_View-Helpers-Doctype.xml" />
        <xi:include href="Zend_View-Helpers-HeadLink.xml" />
        <xi:include href="Zend_View-Helpers-HeadMeta.xml" />
        <xi:include href="Zend_View-Helpers-HeadScript.xml" />
        <xi:include href="Zend_View-Helpers-HeadStyle.xml" />
        <xi:include href="Zend_View-Helpers-HeadTitle.xml" />
        <xi:include href="Zend_View-Helpers-HtmlObject.xml" />
        <xi:include href="Zend_View-Helpers-InlineScript.xml" />
        <xi:include href="Zend_View-Helpers-Json.xml" />
        <xi:include href="Zend_View-Helpers-Translate.xml" />
    </sect2>

    <sect2 id="zend.view.helpers.paths">

        <title>助手的路径</title>

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

        <programlisting role="php"><![CDATA[<?php
$view = new Zend_View();

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


        <para>
            你可以用<code>addHelperPath()</code>来增加helper的路径， <code>Zend_View</code>将使用最近增加的路径。这样你可以使用自己的helper。
        </para>

        <programlisting role="php"><![CDATA[<?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".]]>
        </programlisting>

    </sect2>

    <sect2 id="zend.view.helpers.custom">

        <title>编写自定义的助手类</title>

        <para>
            编写自定义的助手类很容易，只要遵循以下几个原则即可：
        </para>

        <itemizedlist>

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

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

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

            <listitem><para>
                类文件的命名应该是助手类的名称，比如在"specialPurpose"例子中，文件要存为“SpecialPurpose.php”。
            </para></listitem>
        </itemizedlist>

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

        <para>
            下面是一个<code>SpecialPurpose</code> 助手代码的例子：
        </para>

        <programlisting role="php"><![CDATA[<?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);
    }
}]]>
        </programlisting>

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

        <programlisting role="php"><![CDATA[<?php
// remember, in a view script, $this refers to the Zend_View instance.
echo $this->specialPurpose();
echo $this->specialPurpose();
echo $this->specialPurpose();]]>
        </programlisting>

        <para>
            输出结果如下所示：
        </para>
        <programlisting role="php"><![CDATA[I have seen 'The Jerk' 1 time(s).
I have seen 'The Jerk' 2 time(s).
I have seen 'The Jerk' 3 time(s).]]>
        </programlisting>

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

        <programlisting role="php"><![CDATA[<?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);
    }
}]]>
        </programlisting>

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

</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
