Creating Tables with Zend_Form
March 26, 08 by Andrew VayanisI recently published an article trying to shed some light on the Zend_Form component, in particular, when using it with Zend_Config_Ini. In the article I presented a config I developed while trying to learn Zend_Form myself, but unfortunately realized that using generic elementDecorators comes with a price.
Apparently, using elementDecorators overrides individual element level decorators. This leads to some unexpected and annoying results. For instance, in my previous example, I lose the ability to hide or even add individual attributes to specific elements. This leaves me with a form that has labels for each element including the submit button:

The only way I found to correct this problem is to add decorators to each element individually. This is highly redundant and hardly ideal, but it gives the control necessary to fix this problem. It also bloats the config file significantly; previously, the config was 31 lines long, but now it is 47 lines long:
[login] ; General Form Information login.action = "login/submit" login.method = "post" login.id = "login" ; Form Decorators login.decorators.elements.decorator = "FormElements" login.decorators.table.decorator = "HtmlTag" login.decorators.table.options.tag = "table" login.decorators.form.decorator = "Form" ; Username Element login.elements.username.type = "text" login.elements.username.options.label = "Username:" login.elements.username.options.required = true login.elements.username.options.validators.alnum.validator = "alnum" login.elements.username.options.validators.regex.validator = "regex" login.elements.username.options.validators.regex.options.pattern = "/^[a-z]/i" login.elements.username.options.validators.strlen.validator = "StringLength" login.elements.username.options.validators.strlen.options.min = "5" ; Username Decorators login.elements.username.options.decorators.helper = "ViewHelper" login.elements.username.options.decorators.tableData.decorator.td = "HtmlTag" login.elements.username.options.decorators.tableData.options.tag = "td" login.elements.username.options.decorators.label.decorator = "Label" login.elements.username.options.decorators.label.options.tag = "td" login.elements.username.options.decorators.tableRow.decorator.tr = "HtmlTag" login.elements.username.options.decorators.tableRow.options.tag = "tr" ; Password Element login.elements.password.type = "password" login.elements.password.options.label = "Password:" login.elements.password.options.required = true login.elements.password.options.validators.strlen.validator = "StringLength" login.elements.password.options.validators.strlen.options.min = "6" ; Password Decorators login.elements.password.options.decorators.helper = "ViewHelper" login.elements.password.options.decorators.tableData.decorator.td = "HtmlTag" login.elements.password.options.decorators.tableData.options.tag = "td" login.elements.password.options.decorators.label.decorator = "Label" login.elements.password.options.decorators.label.options.tag = "td" login.elements.password.options.decorators.tableRow.decorator.tr = "HtmlTag" login.elements.password.options.decorators.tableRow.options.tag = "tr" ; Submit Form Element login.elements.submit.type = "submit" login.elements.submit.options.label = "Submit" ; Submit Decorators login.elements.submit.options.decorators.helper = "ViewHelper" login.elements.submit.options.decorators.tableData.decorator = "HtmlTag" login.elements.submit.options.decorators.tableData.options.tag = "td" login.elements.submit.options.decorators.label.decorator = "Label" login.elements.submit.options.decorators.label.options.tag = "td" login.elements.submit.options.decorators.label.options.class = "submit" login.elements.submit.options.decorators.tableRow.decorator.tr = "HtmlTag" login.elements.submit.options.decorators.tableRow.options.tag = "tr"
Personally, I think this is poor design and feel it may even be a bug, but I will attempt to submit it to ZF’s issue tracker and see what they say. Otherwise though, I still feel as though Zend_Form is a great component, and will continue to publish any discoveries I come across.
Matthew Weier O'Phinney Says: 04.16.08 at 6:01 pm
Caveat: I’m the author of Zend_Form and Software Architect for Zend Framework.
I’m always skeptical when somebody says, “I think this is poor design” and then doesn’t back it up… How would you do things differently? To Zend_Form, each element is equal, as is each decorator — it doesn’t know how they differ. For the elements, each decorator is equal… it doesn’t know that one is for the label, and one isn’t. Setting decorators for all elements does exactly that — treats them all equally.
Internally, it’s actually *faster* if you pass in your decorators at configuration time. Why? because it doesn’t first set the default decorators and then overwrite them. This is exactly the recommendation I have for developers, as well — pass in decorators at configuration time if you’re not using defaults.
Decorators are meant to allow nested as well as sequential creation of output. In addition, they can draw on *any* metadata in the object they are decorating. Since that metadata is arbitrary, there’s really no extensible way a decorator could hint to an element what it decorates. On the flip side, while decorators can be element aware, doing so sets arbitrary limitations on the decorators and would raise whole new issues.
All that aside, thanks for posting this, as it gives other developers an example for an alternative form layout.
Andrew Vayanis Says: 04.17.08 at 10:42 am
What I would do differently, and granted, I tested this out prior to the official 1.5 release and have not had a chance to test it since, is take declaration order into account . If I set default decorators earlier in the configuration file and then set individual decorators afterwards, the individual decorators should take precedence. To me at least, that would seem like expected behavior of such a component.
*Edit: I also would like to point out, that this post was in no way shape or form meant to put down the work you or anyone else has done on the Zend Framework. I think the direction of the project is great and have been using it for as many projects as I can.
Paris Sánchez Says: 05.05.08 at 4:40 pm
Hello Andrew Vayanis!
Cool article, it was a great guide for me. But, it doesn’t manage description and errors messages, that’s why based on this article and Zend_Form_Decorator manual (http://framework.zend.com/manual/en/zend.form.decorators.html) I build a My_Decorator_TableRow class (attached at the end) whit this class the config file pass from:
; Username Decorators
login.elements.username.options.decorators.helper = “ViewHelper”
login.elements.username.options.decorators.tableData.decorator.td = “HtmlTag”
login.elements.username.options.decorators.tableData.options.tag = “td”
login.elements.username.options.decorators.label.decorator = “Label”
login.elements.username.options.decorators.label.options.tag = “td”
login.elements.username.options.decorators.tableRow.decorator.tr = “HtmlTag”
login.elements.username.options.decorators.tableRow.options.tag = “tr”
To:
; Username Decorators
login.elements.username.options.decorators.tablerow = “TableRow”
I think this make easier to write a large forms
The important think fo this is that in the action controler you have to add ElementPrefixPath before load the config to the form, because ElementPrefixPath is an element you can not pass throu config, acording to ZF documentation.
So the controler and action will look something like this:
addElementPrefixPath(‘My_Decorator’, ‘My/Decorator/’, ‘decorator’);
$config = new Zend_Config_Ini(‘./path/to/configFile.ini’, [login]);
$form->setConfig( $config );
}
…
}
?>
With this I save 6 line per element on confiFile.ini and do not overrides individual element level decorators that has not been defined on My_Decorator_TableRow, but still have to add decorators to each element.
Hope this is useful to someone, and if my English is horrible sorry =P, pleas correct me so i can learn.
Paris.
PS. Here is the complete My_Decorator_TableRow, note: this notation of name use Zend load, and the class shout be in library/My/Decorator/TableRow.php
getElement();
$label = $element->getLabel();
if ($translator = $element->getTranslator()){
$label = $translator->translate($label);
}
if ($element->isRequired()) {
$label = ‘*’ . $label;
}/**/
$label .= ‘:’;
$label = $element->getView()->formLabel($element->getName(), $label);
return ” . $label . ”;
}
public function buildInput()
{
$element = $this->getElement();
$helper = $element->helper;
$input = $element->getView()->$helper(
$element->getName(),
$element->getValue(),
$element->getAttribs(),
$element->options
);
return ” . $input . ”;
}
public function buildErrors()
{
$element = $this->getElement();
$messages = $element->getMessages();
if (empty($messages)) {
return ”;
}
return ” . $element->getView()->formErrors($messages) . ”;
}
public function buildDescription()
{
$element = $this->getElement();
$desc = $element->getDescription();
if (empty($messages)) {
return ”;
}
return ” . $desc . ”;
}
public function render($content)
{
$element = $this->getElement();
if (!$element instanceof Zend_Form_Element) {
return $content;
}
if (null === $element->getView()) {
return $content;
}
$separator = $this->getSeparator();
$placement = $this->getPlacement();
$label = $this->buildLabel();
$input = $this->buildInput();
$errors = $this->buildErrors();
$desc = $this->buildDescription();
$output = ” .
$label .
$input .
” .
$desc .
$errors .
” .
”;
switch ($placement) {
case (self::PREPEND):
return $output . $separator . $content;
case (self::APPEND):
default:
return $content . $separator . $output;
}
}
}
Paris Sánchez Says: 05.05.08 at 4:42 pm
Oops! the comment doesn’t post the php code as i had planed >..<
Andrew Vayanis Says: 05.05.08 at 7:17 pm
Your right, I don’t explain how to add custom validator error messages in this post, and I actually meant to do that in a new post, so I will get around to doing that this week, however if you need some help with it right away, take a look at the comments in my previous post:
http://www.vayanis.com/2008/03/17/using-zend_form-with-zend_config/
Marc Shake Says: 05.20.08 at 7:07 am
Cool article. What I am still trying to do is set up default-values in my config-path. So when I setup a form I want the fields filled with some values.
I am not sure, how to archive that… Any Idea?
Andrew Vayanis Says: 05.20.08 at 6:22 pm
Hey Marc,
If you are looking to use Zend_Config_Ini, you can use something like the following:
elements.username.options.value =””
Alternatively, if you are doing this with a form element object, you should be able to do something like $element->setValue();
gerry22 Says: 05.25.08 at 12:49 pm
There needs to be setButtonDecorators(), setSubmitDecorators() & maybe a setButtonsDecorators() (for setting both together).
You could then extend custom classes with
public function setCustomDecorators()
{
$this->setDecorators($this->_standardDecorators);
$this->setElementDecorators($this->_standardElementDecorator);
$this->setDisplayGroupDecorators($this->_standardGroupDecorator);
$this->setButtonsDecorators($this->_standardButtonDecorator);
}
peter Says: 01.01.09 at 10:39 pm
What configuration you would propose for this type of form?: (specially about array notation)
Menu
ID
Sysname
Action
1
2
peter Says: 01.01.09 at 10:42 pm
Sorry coding issues:(
What configuration you would propose for this type of form?: (specially about array notation)
<form id=”menuList” method=”post” action=”listPost”>
<table>
<tr class=”rowElement”>
<td class=”nameElement” colspan=”4″>Menu</td>
</tr>
<tr class=”rowElement”>
<td class=”titleElement”>
<input type=”hidden” value=”0″ name=”all”/>
<input id=”all” class=”black” type=”checkbox” value=”1″ name=”all”/>
</td>
<td class=”titleElement”>ID</td>
<td class=”titleElement”>Sysname</td>
<td class=”titleElement”>Action</td>
</tr>
<tr class=”rowElement”>
<td class=”grey_10″>
<input type=”hidden” value=”0″ name=”hm_id[1]“/>
<input id=”id-1″ class=”black” type=”checkbox” value=”1″ name=”id[1]“/>
</td>
<td class=”grey_10″>1</td>
<td class=”grey_10″>
<input id=”sys_name-1″ type=”text” value=”admin” name=”sys_name[1]“/>
</td>
<td class=”grey_10″>
<input id=”edit[1]” class=”listButton” type=”submit” value=”Edytuj” name=”edit[1]“/>
<input id=”delete[1]” class=”listButton” type=”submit” value=”Usu?” name=”delete[1]“/>
</td>
</tr>
<tr class=”rowElement”>
<td class=”grey_10″>
<input type=”hidden” value=”0″ name=”id[2]“/>
<input id=”id-2″ class=”black” type=”checkbox” value=”1″ name=”id[2]“/>
</td>
<td class=”grey_10″>2</td>
<td class=”grey_10″>
<input id=”sys_name-2″ type=”text” value=”front” name=”sys_name[2]“/>
</td>
<td class=”grey_10″>
<input id=”edit[2]” class=”listButton” type=”submit” value=”Edytuj” name=”edit[2]“/>
<input id=”delete[2]” class=”listButton” type=”submit” value=”Usu?” name=”delete[2]“/>
</td>
</tr>
<tr class=”rowElement”>
<td class=”barElement” colspan=”4″>
<input id=”deleteSelected” class=”listButton” type=”submit” value=”Usu? wybrane” name=”deleteSelected”/>
<input id=”deleteAll” class=”listButton” type=”submit” value=”Usu? wszystkie” name=”deleteAll”/>
<input id=”saveSelected” class=”listButton” type=”submit” value=”Zapisz wybrane” name=”saveSelected”/>
<input id=”saveAll” class=”listButton” type=”submit” value=”Zapisz wszystkie” name=”saveAll”/>
</td>
</tr>
<tr class=”rowElement”>
<td class=”barElement” colspan=”4″> </td>
</tr>
</table>
</form>
MichelleJD Says: 09.09.09 at 7:42 pm
In my opinion you are not right. I am assured. I can prove it. Write to me in PM, we will talk.
31924
carrepossesseion Says: 09.13.09 at 3:38 pm
As a Newbie, I am always searching online for articles that can help me. Thank you
Bart McLeod Says: 01.18.10 at 12:21 pm
I found your article and I hoped it would help me change the placement option of a decorator, and I tried it using syntax similar to use, but I get an error (using ZF 1.9.6 or very similar).
elements.nieuwsbrief.options.decorators.label.options.placement = “APPEND”
I also tried “append” (lowercase) with no luck. Doing this in the form works fine, but not through config. I get this error:
Fatal error: Uncaught exception ‘Zend_Loader_PluginLoader_Exception’ with message ‘Plugin by name ‘APPEND’ was not found in the registry; used paths: Zend_Form_Decorator_: Zend/Form/Decorator/’ in D:\ZendFramework\library\Zend\Loader\PluginLoader.php:406 Stack trace: #0 D:\ZendFramework\library\Zend\Form\Element.php(1773): Zend_Loader_PluginLoader->load(‘APPEND’) #1 D:\ZendFramework\library\Zend\Form\Element.php(2151): Zend_Form_Element->_getDecorator(‘APPEND’, NULL) #2 D:\ZendFramework\library\Zend\Form\Element.php(1923): Zend_Form_Element->_loadDecorator(Array, ‘placement’) #3 D:\ZendFramework\library\Zend\Form\Element.php(312): Zend_Form_Element->getDecorators() #4 D:\ZendFramework\library\Zend\Form\Element.php(267): Zend_Form_Element->loadDefaultDecorators() #5 D:\ZendFramework\library\Zend\Form.php(1078): Zend_Form_Element->__construct(‘nieuwsbrief’, Array) #6 D:\ZendFramework\library\Zend\Form.php(1009): Zend_Form->createElement(‘checkbox’, ‘nieuwsbrief’, Array) #7 D:\ZendFramework\library\Zend\Form.php(1113): Zend_ in D:\ZendFramework\library\Zend\Loader\PluginLoader.php on line 406
Apparently ZF is looking for a plugin, while I just want to set an option on the decorator. Any clues what I am doing wrong? I will search for other articles of course, and check the mailing list. But I thought people might benefit if you can come up with an answer here.
Thank you!
Andrew Vayanis Says: 01.19.10 at 4:29 pm
I would have to try it out to confirm, but I think it has to do with you putting the options on the label which doesn’t take any options I don’t think. Have you tried:
elements.nieuwsbrief.options.decorators.placement = “append”?
Bart McLeod Says: 01.20.10 at 6:34 am
I think I tried this kind of variations, but with no luck. It also makes no sense. The label decorator does take this kind of options, I am postive about that.
If I take the form as is, created throug .ini and after that I do
$form->nieuwsbrief->getDecorator(‘Label’)->setOption(‘placement’, ‘APPEND’);
it works fine.
So the option can be set for sure.
I found an entry in the mailing list that is somewhat instructieve, but does yield the same result. This assumes you should talk to the decorators array like this:
elements.decorators.5.options.placement = “APPEND”
The label is the fith default decorator if you dump getDecorators() on the nieuwsbrief element. There is however no difference in the result. It would be valuable if you tried, it is possible after all that a bug has entered the system somewhere.
Andrew Vayanis Says: 01.24.10 at 2:25 am
Hey,
I haven’t played with the placement options too much, but I was able to set a placement option on a label by doing this:
elements.nieuwsbrief.options.label.options.placement = “append”
instead of:
elements.nieuwsbrief.options.decorators.label.options.placement = “APPEND”
Notice I took out the ‘decorators’ specification between options and label. This makes sense as it is reflected by the non-config code you pasted that does work.
Bart McLeod Says: 01.29.10 at 9:14 am
This helps in so far that I get a different exception AND it tries to set the placement option, but it fails, because it claims to have one already:
Fatal error: Uncaught exception ‘Zend_Config_Exception’ with message ‘Cannot create sub-key for ‘label’ as key already exists’ in D:\ZendFramework\library\Zend\Config\Ini.php:293 Stack trace: #0 D:\ZendFramework\library\Zend\Config\Ini.php(295): Zend_Config_Ini->_processKey(Array, ‘label.options.p…’, ‘append’) #1 D:\ZendFramework\library\Zend\Config\Ini.php(295): Zend_Config_Ini->_processKey(Array, ‘options.label.o…’, ‘append’) #2 D:\ZendFramework\library\Zend\Config\Ini.php(295): Zend_Config_Ini->_processKey(Array, ‘nieuwsbrief.opt…’, ‘append’) #3 D:\ZendFramework\library\Zend\Config\Ini.php(260): Zend_Config_Ini->_processKey(Array, ‘elements.nieuws…’, ‘append’) #4 D:\ZendFramework\library\Zend\Config\Ini.php(134): Zend_Config_Ini->_processSection(Array, ‘form’) #5 D:\werk\cuddlefish\application\default\controllers\helpers\ContactForm.php(81): Zend_Config_Ini->__construct(‘../config/modul…’) #6 D:\werk\cuddlefish\application\default\controllers\helpers\ContactForm.php(19): Default_Action_Helper_ContactForm->_ge in D:\ZendFramework\library\Zend\Config\Ini.php on line 293
If I remove the key that set the text on the label, I get no exception, but this output:
Array
Note the Array label I have now gotten. It is the result of:
elements.nieuwsbrief.options.label.options.placement = “append”
So it did not help, but I appreciate your effort!
Bart McLeod Says: 01.29.10 at 9:15 am
The html did not come out as expected….
Zend Framework Johny Says: 03.09.10 at 6:08 am
Article helped me a lot, thanks.
Facebook Layouts Says: 03.12.10 at 1:58 am
Thanks for the useful info…we are always looking for new blogs to link to.