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