2011-09-25 44 views
10

我对此很陌生,zend装饰malarchy,但我有两个重要的问题,我无法得到我的头。问题一其次是一些例子Zend_Framework装饰工具将标签和ViewHelper包装在一个div中

$decorate = array(
    array('ViewHelper'), 
    array('Description'), 
    array('Errors', array('class'=>'error')), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')), 
); 

... 

$name = new Zend_Form_Element_Text('title'); 
$name->setLabel('Title') 
    ->setDescription("No --- way"); 

$name->setDecorator($decorate); 

,输出

<li class="element"> 
    <label for="title" class="required">Title</label> 
    <input type="text" name="title" id="title" value=""> 
    <p class="hint">No --- way</p> 
    <ul class="error"> 
     <li>Value is required and can't be empty</li> 
    </ul> 
</li> 

问题#1

我怎么裹label和周围div标签的input?所以输出如下:

<li class="element"> 
    <div> 
     <label for="title" class="required">Title</label> 
     <input type="text" name="title" id="title" value=""> 
    </div> 
    <p class="hint">No --- way</p> 
    <ul class="error"> 
     <li>Value is required and can't be empty</li> 
    </ul> 
</li> 

问题#2

elements$decorate排列的顺序是什么呢? 他们没有感觉!

回答

21

decorator pattern是一种设计模式,用于在不改变现有类的情况下向现有类添加功能。相反,装饰类将自身封装在另一个类中,并且通常将与装饰类相同的接口公开。

基本例如:

interface Renderable 
{ 
    public function render(); 
} 

class HelloWorld 
    implements Renderable 
{ 
    public function render() 
    { 
     return 'Hello world!'; 
    } 
} 

class BoldDecorator 
    implements Renderable 
{ 
    protected $_decoratee; 

    public function __construct(Renderable $decoratee) 
    { 
     $this->_decoratee = $decoratee; 
    } 

    public function render() 
    { 
     return '<b>' . $this->_decoratee->render() . '</b>'; 
    } 
} 

// wrapping (decorating) HelloWorld in a BoldDecorator 
$decorator = new BoldDecorator(new HelloWorld()); 
echo $decorator->render(); 

// will output 
<b>Hello world!</b> 

现在,你可能会认为,由于Zend_Form_Decorator_*类是装饰,并有render方法,该装置自动装饰类render法输出将始终由装饰者用额外的内容包装。但在我们上面的基本范例的考察,我们可以很容易地看到,这并不一定是在所有课程的情况下,通过这种额外的(虽然没什么用)例所示:

class DivDecorator 
    implements Renderable 
{ 
    const PREPEND = 'prepend'; 
    const APPEND = 'append'; 
    const WRAP = 'wrap'; 

    protected $_placement; 

    protected $_decoratee; 

    public function __construct(Renderable $decoratee, $placement = self::WRAP) 
    { 
     $this->_decoratee = $decoratee; 
     $this->_placement = $placement; 
    } 

    public function render() 
    { 
     $content = $this->_decoratee->render(); 
     switch($this->_placement) 
     { 
      case self::PREPEND: 
       $content = '<div></div>' . $content; 
       break; 
      case self::APPEND: 
       $content = $content . '<div></div>'; 
       break; 
      case self::WRAP: 
      default: 
       $content = '<div>' . $content . '</div>'; 
     } 

     return $content; 
    } 
} 

// wrapping (decorating) HelloWorld in a BoldDecorator and a DivDecorator (with DivDecorator::APPEND) 
$decorator = new DivDecorator(new BoldDecorator(new HelloWorld()), DivDecorator::APPEND); 
echo $decorator->render(); 

// will output 
<b>Hello world!</b><div></div> 

这是事实上基本上装饰者的工作方式很多,如果他们有这个安置功能是有道理的。

对于有意义的修饰器,例如,您可以使用setOption('placement', 'append')来控制位置,或者通过将选项'placement' => 'append'传递给选项数组。

对于Zend_Form_Decorator_PrepareElements,例如,此放置选项是无用的,为此忽略,因为它准备由ViewScript装饰中使用的形式的元件,使得它不接触的装饰元素的呈现内容装饰器的一个。

根据各个装饰器的默认功能,装饰类的内容被包装,追加,前置,丢弃对装饰类完全不同的东西,不需要直接添加内容,然后将内容传递给下一个装饰器。考虑一个简单的例子:

class ErrorClassDecorator 
    implements Renderable 
{ 
    protected $_decoratee; 

    public function __construct(Renderable $decoratee) 
    { 
     $this->_decoratee = $decoratee; 
    } 

    public function render() 
    { 
     // imagine the following two fictional methods 
     if($this->_decoratee->hasErrors()) 
     { 
      $this->_decoratee->setAttribute('class', 'errors'); 
     } 

     // we didn't touch the rendered content, we just set the css class to 'errors' above 
     return $this->_decoratee->render(); 
    } 
} 

// wrapping (decorating) HelloWorld in a BoldDecorator and an ErrorClassDecorator 
$decorator = new ErrorClassDecorator(new BoldDecorator(new HelloWorld())); 
echo $decorator->render(); 

// might output something like 
<b class="errors">Hello world!</b> 

现在,当您设置的装饰为Zend_Form_Element_*元素,它们将被包装,因此执行,在它们被添加的顺序。所以,去你的榜样:

$decorate = array(
    array('ViewHelper'), 
    array('Description'), 
    array('Errors', array('class'=>'error')), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')), 
); 

...基本上会发生什么是以下(实际的类名截断简洁):

$decorator = new HtmlTag(new Label(new Errors(new Description(new ViewHelper())))); 
echo $decorator->render(); 

因此,在检查你的榜样输出,我们应该能够提取各个装饰者的默认放置行为:

// ViewHelper->render() 
<input type="text" name="title" id="title" value=""> 

// Description->render() 
<input type="text" name="title" id="title" value=""> 
<p class="hint">No --- way</p> // placement: append 

// Errors->render() 
<input type="text" name="title" id="title" value=""> 
<p class="hint">No --- way</p> 
<ul class="error"> // placement: append 
    <li>Value is required and cant be empty</li> 
</ul> 

// Label->render() 
<label for="title" class="required">Title</label> // placement: prepend 
<input type="text" name="title" id="title" value=""> 
<p class="hint">No --- way</p> 
<ul class="error"> 
    <li>Value is required and cant be empty</li> 
</ul> 

// HtmlTag->render() 
<li class="element"> // placement: wrap 
    <label for="title" class="required">Title</label> 
    <input type="text" name="title" id="title" value=""> 
    <p class="hint">No --- way</p> 
    <ul class="error"> 
     <li>Value is required and cant be empty</li> 
    </ul> 
</li> 

你知道什么;这实际上是所有各个装饰器的默认放置。

但是现在出现了困难的部分,我们需要做些什么来获得您要查找的结果?为了包裹labelinput,我们不能简单地这样做:

$decorate = array(
    array('ViewHelper'), 
    array('Description'), 
    array('Errors', array('class'=>'error')), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), 
    array('HtmlTag', array('tag' => 'div')), // default placement: wrap 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')), 
); 

...因为这将包装所有前面的内容(ViewHelperDescriptionErrorsLabel)有一个div,对不对?甚至没有...添加的装饰器将被替换为下一个装饰器,因为如果它是同一个类,装饰器将被替换为下面的装饰器。在代替你必须给它一个唯一的密钥:

$decorate = array(
    array('ViewHelper'), 
    array('Description'), 
    array('Errors', array('class'=>'error')), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), 
    array(array('divWrapper' => 'HtmlTag'), array('tag' => 'div')), // we'll call it divWrapper 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')), 
); 

现在,我们仍然面临着divWrapper将包装所有前面的内容(ViewHelperDescriptionErrorsLabel)的问题。所以我们需要在这里创意。有很多方法可以实现我们想要的。我举一个例子,这可能是最简单的:

$decorate = array(
    array('ViewHelper'), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), // default placement: prepend 
    array(array('divWrapper' => 'HtmlTag'), array('tag' => 'div')), // default placement: wrap 
    array('Description'), // default placement: append 
    array('Errors', array('class'=>'error')), // default placement: append 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')), // default placement: wrap 
); 

有关Zend_Form装饰我建议你阅读Zend框架的首席开发人员马修·威尔O'Phinney的article about Zend Form Decorators

+0

哇,这是一些完整的答案!好一个 ! –

+2

哦哇,我有一个答案在那个答案,谢谢并接受:D –

+0

P.S这是最有趣的回答有史以来有 –

2

问题#1

更改装饰订购并添加HtmlTag助手这种方式:

$decorate = array(
    array('ViewHelper'), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), 
    array('HtmlTag', array('tag' => 'div')), 
    array('Description'), 
    array('Errors', array('class'=>'error')), 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')) 
); 

问题#2

装饰器链,的输出每一个都被传递给下一个的输入,以便被它“装饰”。

默认情况下,它们会附加内容(描述,错误),附加内容(标签..)和或包装(HtmlTag)。但这些都是默认的行为,你可以改变它的大部分:

array('HtmlTag', array('tag' => 'span', placement=>'APPEND')); 
//this would append <span></span> to the output of the previous decorator instead of wrapping it inside the <span> 

让我们更仔细看看你的链条会发生什么:

  1. ViewHelper使用它使你的表单元素默认viewHelper,在表单元素的类中声明。

  2. Label预先将标签到先前输出

  3. HtmlTag包装一个<div>周围

  4. Description追加元素描述

  5. Errors附加的错误信息,如果有的话

  6. HtmlTag包装这一切都在<li>

编辑

我写这个答案没有任何的测试,所以有可能在这里和那里是一些小的错误。亲爱的读者,如果你看到一些只是删除评论,我会更新。

+0

很好的回答更多的解释,以及当然!唯一需要注意的是,HtmlTag装饰器中的一个或者甚至两者(实际上不确定)应该添加一个唯一的键'array('someUniqueKey'=>'HtmlTag')',否则最后一个会替换前一个。 –

+0

哇谢谢。 ....:D –