2011-09-25 55 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 –