在本系列关于Zend_Form
装饰器的上一期中,我研究了如何组合装饰器来创建复杂的输出。在那篇文章中,我注意到虽然您可以通过这种方法获得很大的灵活性,但它也会增加一些复杂性和开销。在本文中,我将向您展示如何单独呈现装饰器,以便为您的表单和/或单个元素创建自定义标记。
一旦你注册了你的装饰器,你可以稍后通过名称从元素中检索它们。让我们回顾一下我们之前的例子:
$element = new Zend_Form_Element('foo', array( 'label' => 'Foo', 'belongsTo' => 'bar', 'value' => 'test', 'prefixPath' => array('decorator' => array( 'My_Decorator' => 'path/to/decorators/', )), 'decorators' => array( 'SimpleInput' array('SimpleLabel', array('placement' => 'append')), ), ));
如果我们只想拉取和渲染“SimpleInput”装饰器,我们可以使用getDecorator()
方法:
$decorator = $element->getDecorator('SimpleInput'); echo $decorator->render('');
这很简单,但还可以做得更简单;让我们在一行中完成:
echo $element->getDecorator('SimpleInput')->render('');
还不错,但还是有点复杂。为了使这更容易,我们在1.7中的Zend_Form
中引入了一种速记符号:您现在可以通过调用方法renderDecoratorName()
来呈现任何已注册的装饰器。这将有效地执行您在上面看到的内容,但使$content
参数成为可选参数并简化了用法:
echo $element->renderSimpleInput();
这是一个绝妙的技巧,但是您将如何使用它以及为什么要使用它呢?
许多开发人员和设计人员对其表单有非常精确的标记需求。他们宁愿完全控制输出,也不愿依赖可能符合或可能不符合他们设计的自动化程度更高的解决方案。在其他情况下,表单布局可能需要大量专门的标记——对任意元素进行分组,使某些元素不可见,除非选择了特定链接,等等。
让我们利用渲染单个装饰器的能力来创建一些专门的标记。
首先,让我们定义一个表单。我们的表单将捕获用户的人口统计详细信息。标记将是高度自定义的,并且在某些情况下直接使用视图助手而不是表单元素来实现其目标。这是基本的表单定义:
class My_Form_UserDemographics extends Zend_Form { public function init() { // Add a path for my own decorators $this->addElementPrefixPaths(array( 'decorator' => array('My_Decorator' => 'My/Decorator'), )); $this->addElement('text', 'firstName', array( 'label' => 'First name: ', )); $this->addElement('text', 'lastName', array( 'label' => 'Last name: ', )); $this->addElement('text', 'title', array( 'label' => 'Title: ', )); $this->addElement('text', 'dateOfBirth', array( 'label' => 'Date of Birth (DD/MM/YYYY): ', )); $this->addElement('text', 'email', array( 'label' => 'Your email address: ', )); $this->addElement('password', 'password', array( 'label' => 'Password: ', )); $this->addElement('password', 'passwordConfirmation', array( 'label' => 'Confirm Password: ', )); } }
注意:此时我没有定义任何验证器或过滤器,因为它们与装饰的讨论无关。在真实场景中,您应该定义它们。
说完这些,让我们考虑一下我们可能想要如何显示这个表单。一个常见的关于名字/姓氏的习惯用法是将它们显示在一行上;当提供标题时,它通常也在同一行上。如果不使用JavaScript日期选择器,日期通常会分成三个并排显示的字段。
让我们使用逐个渲染元素装饰器的功能来完成此操作。首先,我会注意到我没有为给定的元素设置任何显式装饰器。作为复习,(大多数)元素的默认装饰器是:
- ViewHelper:利用视图助手来呈现表单输入
- 错误:利用FormErrors视图助手来呈现验证错误
- 描述:利用FormNote视图助手来呈现表单输入呈现元素描述(如果有)
- HtmlTag:将以上三项包装在
- Label:使用FormLabel视图助手(并将其包装在
此外,作为回顾,您可以像访问类属性一样访问表单的任何元素;只需按您指定的名称引用该元素即可。
我们的视图脚本可能如下所示:
<?php $form = $this->form; // Remove <dt> from label generation foreach ($form->getElements() as $element) { $element->getDecorator('label')->setTag(null); } ?> <form method="<?php echo $form->getMethod() ?>" action="<?php echo $form->getAction()?>"> <div class="element"> <?php echo $form->title->renderLabel() . $form->title->renderViewHelper() ?> <?php echo $form->firstName->renderLabel() . $form->firstName->renderViewHelper() ?> <?php echo $form->lastName->renderLabel() . $form->lastName->renderViewHelper() ?> </div> <div class="element"> <?php echo $form->dateOfBirth->renderLabel() ?> <?php echo $this->formText('dateOfBirth['day']', '', array( 'size' => 2, 'maxlength' => 2)) ?> / <?php echo $this->formText('dateOfBirth['month']', '', array( 'size' => 2, 'maxlength' => 2)) ?> / <?php echo $this->formText('dateOfBirth['year']', '', array( 'size' => 4, 'maxlength' => 4)) ?> </div> <div class="element"> <?php echo $form->password->renderLabel() . $form->password->renderViewHelper() ?> </div> <div class="element"> <?php echo $form->passwordConfirmation->renderLabel() . $form->passwordConfirmation->renderViewHelper() ?> </div> <?php echo $this->formSubmit('submit', 'Save') ?> </form>
如果你使用上面的视图脚本,你将得到大致如下的HTML(近似值,因为下面的HTML是格式化的):
<form method="post" action=""> <div class="element"> <label for="title" tag="" class="optional">Title:</label> <input type="text" name="title" id="title" value=""/> <label for="firstName" tag="" class="optional">First name:</label> <input type="text" name="firstName" id="firstName" value=""/> <label for="lastName" tag="" class="optional">Last name:</label> <input type="text" name="lastName" id="lastName" value=""/> </div> <div class="element"> <label for="dateOfBirth" tag="" class="optional">Date of Birth (DD/MM/YYYY):</label> <input type="text" name="dateOfBirth[day]" id="dateOfBirth-day" value="" size="2" maxlength="2"/> / <input type="text" name="dateOfBirth[month]" id="dateOfBirth-month" value="" size="2" maxlength="2"/> / <input type="text" name="dateOfBirth[year]" id="dateOfBirth-year" value="" size="4" maxlength="4"/> </div> <div class="element"> <label for="password" tag="" class="optional">Password:</label> <input type="password" name="password" id="password" value=""/> </div> <div class="element"> <label for="passwordConfirmation" tag="" class="optional">Confirm Password:</label> <input type="password" name="passwordConfirmation" id="passwordConfirmation" value=""/> </div> <input type="submit" name="submit" id="submit" value="Save"/> </form>
看起来像下面的屏幕截图:
也许不是很漂亮,但是通过一些CSS,它可以看起来完全符合您的要求。然而,要点是这个表单几乎完全是使用自定义标记生成的,同时仍然利用装饰器来实现最常见的标记(并确保发生诸如使用html实体转义和值注入之类的事情)。
到本系列的这一点,您应该已经相当熟悉使用Zend_Form
的装饰器的标记可能性。在下一期中,我将重新审视上面的日期元素,并演示如何为复合元素创建自定义元素和装饰器。
本系列的其他内容:
- 最简单的Zend_Form装饰器
- 由内而外:如何对装饰器进行分层