读书人

Zend_Form 装点器

发布时间: 2012-09-21 15:47:26 作者: rapoo

Zend_Form 装饰器
Zend_Form 装饰器

Zend_Form 作为Zend Framework新增的组件,已经受到了一致的好评,在表单问题上有了一个灵活的解决方案。它的灵活性在于,它提供了已经被证实的对很多开发着来说的一个痛点:装饰器。这个教程的目的是分享装饰器上面得到的一些启发,同时也提供了一些解决方案:创建你自己的装饰器,并且把它们通过创造性的方法结合起来,最终输出你自己生成的表单。

?

背景

当设计Zend_Form时,一个主要的目标是生成必要的标签去展示每个元素和表单本身。基本原理是把值和元数据注入到元素标签并且报告错误,这些操作经常是很乏味的,重复的工作。所有的表单方案都要解决这个问题并且做这些琐碎的事。

?

当然,现在对于生成表单标签的问题还没有一个标准的做法去解决它,他们的方法随着开发者和项目的不同差别也会很大,所以任何的解决方案都必须足够灵活以适应各种方法。

?

另一个值得注意的是Zend_Form没有绑定到Zend_View,这样的话,默认的装饰器实际利用Zend_View,这个解决方案需要足够灵活以至一个开发者选择不使用Zend_View,他们确实做到了。

?

心里揣着这些目标,解决方案自然也就呈现出来了:使用Decorator Pattern(http://en.wikipedia.org/wiki/Decorator_pattern)的原则。通过包装它来扩张这个类的功能,这就允许开发者通过调用相同的API来添加或者修改已经存在的特性。装饰器可能被其他装饰器包装,允许以一种分层的方法去扩展,装饰器本身被使用来代替原有的对象,并且他保持了同样的API;它仅仅提供了新建或者添加的方法到原作。

?

例如,设计一个窗口类,你可能创建一个窗口装饰器来显示这个窗口,并且添加装饰器来显示滚动条,窗口标题,等等;每个装饰器负责最终展示的一个方面,可能会像下面这样:

?

?

$window = new WindowScrollbarDecorator(            new WindowTitleDecorator(            new WindowDecorator( new Window() )  ) );

?

?

为了呈现最终的窗口:

$window->render(); 它可能会执行WindowScrollbarDecorator::render()方法,接着调用WindowTitleDecorator::render(),然后接着调用WindowDecorator::render(),它最终也会调用Window::render()方法,或者只是简单的使用对象状态,最终的结果就是一个带有标题和滚动条的窗口了。

?

装饰器是目前Zend_Form理想的解决方案,在使用它时,开发者可以选择性的决定在最终输出的时候显示什么,最终的解决方案在于可以修改的装饰器上;我们没有去装饰这个对象,而是装饰生成的内容,但是我们在装饰内容的时候使用的是元数据,他们来自元素或者表单对象,而传统的装饰器从外到内工作,Zend_Form的工作是从内到外的,分层的内容向外。

基础操作

当装饰的时候,你可以把传入的内容进行前置,附加,或者替换(应该还包括),初始化的内容是一个空字符串,每个装饰器作用在在它上一个装饰器的执行结果上。

我们来看一下啊默认的装饰器是如何工作的,默认的装饰器是ViewHelper,Errors,HtmlTag和Label。默认的,ViewHelper装饰器替换所提供的所有内容,Error装饰器附加提供的内容,HtmlTag装饰器包装所提供的内容,Label装饰器前置提供的内容(除了ViewHelper以为的位置-附加,前置,还是包裹是可以配置的)想想一下执行结果,最终的执行会看起来像这样:

?

?

$label->render($htmlTag->render($errors->render($viewHelper->render(''))))

?

?

让我们一步一步的看看内容是如何构建的:

?

?

  <input name="foo" id="foo" type="text" value="" />  <input name="foo" id="foo" type="text" value="" />        <div id="foo" type="text" value="" />        <div id="foo" type="text" value="" />        <div name="code">$label = $element->getDecorator('label');$label->setOption('placement', 'append');
?

?

大部分装饰器都有很多的参数,你需要去读手册或者API文档去得到更完整的细节。

?

另一个简单的定制输出的办法是移除一个装饰器,加入你不想显示一个标签,你可以移除这个装饰器

?

?

$element->removeDecorator('label');?

?

?

很不幸的是,当前还没有办法在装饰器堆栈的一个特殊位置插入一个装饰器,所以假如你需要在堆栈中插入一个新的装饰器,最好的办法就是重置堆栈,例如。假如你想添加一个Description装饰器在元素后面,你可以这样做:

?

?

$element->setDescription($desc); $element->setDecorators(array( 'ViewHelper',  'Description', 'Errors',  array('HtmlTag', array('tag' => 'dd')),  array('Label', array('tag' => 'dt')), ));
?

?

尽管addDecorator()和addDecorators() 方法存在,但通常还是要用setDecorators() 除非你需要很少的装饰器。

?

?

当添加装饰器的时候,你可以给他设置别名,这样做可以帮助你使用不同的名字来存储装饰器--可以帮你在堆栈中通过名字来检索到它,当你想添加两个以上的同样类型的装饰器的时候这是非常有用的;实际上在这种情况下,假如你没有设置别名,最后注册的同类型的装饰器会覆盖其它所有的实例!你可以通过传递一个数组作为装饰器的类型来完成别名的命名,这个数组是一个键/值对,键就是装饰器的别名,值是装饰器的类型。例如,假如你想使用两个不同的HTML标签在你的堆栈,你可以这样做:

?

?

$element->setDecorators(array(    'ViewHelper',     'Description',    'Errors',    array(array('elementDiv' => 'HtmlTag'),     array('tag' => 'div')),     array(array('td' => 'HtmlTag'), array('tag' => 'td')),    array('Label', array('tag' => 'td')), ));
?

?

在上面的例子中,元素内容将被HTML div包裹,它返回的内容再被包裹进一个表格,这两个的别名分别被设置成‘elementDiv’ 和‘td’。

?

标准装饰器

既然我们已经知道了如何操纵装饰器堆栈和个别的装饰器,那标准的装饰器是怎样的?

读书人网 >编程

热点推荐