I am new to this, zend decoration malarchy, but i have two significant questions that i cant get my head around. Question one is followed by some example
$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);
Which outputs
<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>
The Question #1
How do i wrap the label
and the input
around a div tag? So the output is as follows:
<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>
The Question #2
What is up with the order of the elements
in the $decorate
array? They MAKE NO SENSE!
The decorator pattern is a design pattern for adding functionality to existing classes without altering those existing classes. In stead, a decorator class wraps itself around another class, and generally exposes the same interface as the decorated class.
Basic example:
Now, you might be tempted to think that because the
Zend_Form_Decorator_*
classes are decorators, and have arender
method, this automatically means the output of the decorated class'render
method will always be wrapped with additional content by the decorator. But on inspection of our basic example above, we can easily see this doesn't necessarily have to be the case at all of course, as illustrated by this additional (albeit fairly useless) example:This is in fact basically how a lot of
Zend_Form_Decorator_*
decorators work, if it makes sense for them to have this placement functionality.For decorators where it makes sense, you can control the placement with the
setOption( 'placement', 'append' )
for instance, or by passing the option'placement' => 'append'
to the options array, for instance.For
Zend_Form_Decorator_PrepareElements
, for instance, this placement option is useless and therefor ignored, as it prepares form elements to be used by aViewScript
decorator, making it one of the decorators that doesn't touch the rendered content of the decorated element.Depending on the default functionality of the individual decorators, either the content of the decorated class is wrapped, appended, prepended, discarded or something completely different is done to the decorated class, without adding something directly to the content, before passing along the content to the next decorator. Consider this simple example:
Now, when you set the decorators for a
Zend_Form_Element_*
element, they will be wrapped, and consequently executed, in the order in which they are added. So, going by your example:... basically what happens is the following (actual class names truncated for brevity):
So, on examining your example output, we should be able to distill the default placement behaviour of the individual decorators:
And what do you know; this actually is the default placement of all respective decorators.
But now comes the hard part, what do we need to do to get the result you are looking for? In order to wrap the
label
andinput
we can't simply do this:... as this will wrap all preceding content (
ViewHelper
,Description
,Errors
andLabel
) with a div, right? Not even... the added decorator will be replaced by the next one, as decorators are replaced by a following decorator if it is of the same class. In stead you would have to give it a unique key:Now, we're still faced with the problem that
divWrapper
will wrap all preceding content (ViewHelper
,Description
,Errors
andLabel
). So we need to be creative here. There's numerous ways to achieve what we want. I'll give one example, that probably is the easiest:For more explanation about
Zend_Form
decorators I'd recommend reading Zend Framework's lead developer Matthew Weier O'Phinney's article about Zend Form DecoratorsQuestion #1
Change the decorators order and add an HtmlTag helper this way :
Question #2
Decorators are a chain, the output of each one is passed to the input of the next one, in order to be 'decorated' by it.
By default, they append content (description, errors), prepend content (label..) and or wrap something around (HtmlTag). But these are default behaviors, and you can change it for most of them :
Let's have a more closer look to what happens in your chain :
ViewHelper
renders your form element using it's default viewHelper, declared in the form element's class.Label
prepends the label to the previous outputHtmlTag
wraps a<div>
aroundDescription
appends the elements descriptionErrors
appends error messages, if anyHtmlTag
wraps all this in an<li>
EDIT
I wrote this answer without testing anything, so there might be some little inaccuracies here and there. Dear reader, if you see some just drop a comment and i'll update.