2011-09-25 8 views
10

Sono nuovo, la malaria di decorazione di zend, ma ho due domande significative che non riesco a capirmi. Prima domanda è seguito da alcuni esempiZend_Framework Decorators Wrap Label e ViewHelper all'interno di un 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); 

quali uscite

<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> 

La Domanda # 1

Come posso avvolgere il label e la input attorno a un tag div? Quindi l'uscita è la seguente:

<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> 

La Domanda # 2

Qual è il passo con l'ordine del elements nella matrice $decorate? NON FANNO SENSO!

risposta

21

decorator pattern è uno schema di progettazione per l'aggiunta di funzionalità a classi esistenti senza alterare quelle classi esistenti. Invece, una classe decoratrice si avvolge attorno ad un'altra classe e generalmente espone la stessa interfaccia della classe decorata.

Esempio di base:

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> 

Ora, si potrebbe essere tentati di pensare che, poiché i Zend_Form_Decorator_* classi sono decoratori, e avere un metodo render, questo significa automaticamente l'uscita della classe decorato render metodo sarà sempre essere avvolto con contenuti aggiuntivi dal decoratore. Ma su ispezione del nostro esempio di base di cui sopra, si può facilmente vedere questo non deve necessariamente essere il caso a tutti naturalmente, come illustrato da questo ulteriore (ancorché alquanto inutile) Esempio:

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> 

Questo è in Infatti, in pratica, funzionano un sacco di decoratori Zend_Form_Decorator_*, se ha senso che abbiano questa funzionalità di posizionamento.

Per i decoratori in cui ha senso, è possibile controllare il posizionamento con setOption('placement', 'append'), ad esempio, oppure passare l'opzione 'placement' => 'append' all'array delle opzioni, ad esempio.

Per Zend_Form_Decorator_PrepareElements, per esempio, questa opzione di posizionamento è inutile e per questo ignorato, che si prepara elementi del modulo per essere utilizzati da un ViewScript decoratore, che lo rende uno dei decoratori che non tocca il contenuto reso dell'elemento decorato .

seconda della funzionalità predefinita dei singoli decoratori, né il contenuto della classe decorato è avvolto, aggiunto, anteporre, scartati o qualcosa di completamente diverso viene fatto alla classe decorato, senza aggiungere qualcosa direttamente al contenuto, prima di passare il contenuto al prossimo decoratore. Considerate questo semplice esempio:

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> 

Ora, quando si impostano i decoratori per un elemento Zend_Form_Element_*, saranno avvolti, e di conseguenza eseguiti, nell'ordine in cui vengono aggiunti.Così, andando con il vostro esempio:

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

... in fondo ciò che accade è la seguente (nomi di classe attuale troncati per brevità):

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

Così, per l'esame di uscita ad esempio, dovremmo essere in grado di distillare il comportamento di posizionamento predefinito dei singoli decoratori:

// 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> 

E che ne sai; questo in realtà è il posizionamento predefinito di tutti i rispettivi decoratori.

Ma ora arriva la parte difficile, cosa dobbiamo fare per ottenere il risultato che stai cercando? Al fine di avvolgere il label e input non possiamo semplicemente fare questo:

$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')), 
); 

... come questo avvolgere tutto il contenuto precedente (ViewHelper, Description, Errors e Label) con un div, giusto? Nemmeno ... l'addetto alla decorazione aggiunto sarà sostituito dal successivo, poiché i decoratori sono sostituiti da un decoratore successivo se è della stessa classe. In vece si dovrà dare una chiave univoca:

$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')), 
); 

Ora, siamo ancora di fronte al problema che divWrapper lo avvolgerà tutti precedente contenuto (ViewHelper, Description, Errors e Label). Quindi dobbiamo essere creativi qui. Esistono numerosi modi per ottenere ciò che vogliamo. Ti do un esempio, che probabilmente è il più facile:

$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 
); 

Per ulteriori spiegazioni su Zend_Form decoratori Mi raccomando di leggere article about Zend Form Decorators

+0

Wow, questa è una risposta completa! Ben fatto ! –

+2

Oh wow, ho avuto un momento ahaa in quella risposta, Grazie e accettato: D –

+0

P.S è stata la risposta più sorprendente che abbia mai avuto –

2

Domanda # 1

Cambiare i decoratori ordinare e aggiungere un aiutante HtmlTag questo modo:

$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')) 
); 

Domanda # 2

decoratori sono una catena, l'uscita del ognuno è passato all'ingresso del prossimo, per essere 'decorato' da esso.

Per impostazione predefinita, aggiungono contenuto (descrizione, errori), anteporre contenuto (etichetta ..) e o avvolgere qualcosa intorno (HtmlTag). Ma questi sono comportamenti predefiniti, e si può cambiare per la maggior parte di loro:

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> 

Diamo uno sguardo più vicino a ciò che accade nella vostra catena:

  1. ViewHelper rende il vostro elemento modulo usando il suo viewHelper predefinito, dichiarato nella classe dell'elemento del modulo.

  2. Label antepone l'etichetta all'uscita precedente

  3. HtmlTag avvolge una <div> intorno

  4. Description aggiunge la descrizione elementi

  5. Errors accoda messaggi di errore, se presente

  6. HtmlTag avvolge tutto questo in un <li>

EDIT

ho scritto questa risposta senza prove niente, quindi ci potrebbero essere alcuni piccoli imprecisioni qua e là. Caro lettore, se ne vedi qualcuno basta lasciare un commento e aggiornerò.

+0

Buona risposta di sviluppatore principale di Zend Framework Matthew Weier O'Phinney oltre naturalmente ! Unica avvertenza è che uno o forse anche entrambi (non è sicuro) dei decoratori di 'HtmlTag' dovrebbero essere aggiunti con una chiave unica' array ('someUniqueKey' => 'HtmlTag') ', altrimenti l'ultimo sostituirà il precedente . –

+0

wow grazie. ....: D –