Help me understand Zend Form decorators

I don’t code php every day unfortunately, usually busy with other stuff. But now and then I drop by to do stuff - and lately I’ve played with zend framework. Great stuff.

So far I’ve avoided decorators in zend form, cause I don’t understand jack about them.
Time has come to try understand this monster - but I don’t have time to do 3days of reading source code of decorators and experiment.

First: simple form.


class SelectFylkeForm extends Zend_Form
{
	public function __construct($options = null )
	{
		parent::__construct($options);
		$this->setName('Select');

		$items = array( 'a' => 'Choose A', 'b' => 'Choose B', 'c' => 'Choose C');
				
		$elements = array();
		
		$select = new Zend_Form_Element_Select( 'items' );
		$select->setLabel( 'Items' )
			   ->setMultiOptions( $items )
			   ->setRequired(true)->addValidator('NotEmpty', true);
		$select->setValue( $items['a'] );
		
		$submit = new Zend_Form_Element_Submit('submit');
		$submit->setLabel('Choose');

		$elements[] = $select;
		$elements[] = $submit;
		
		$this->addElements( $elements );		
	}
}

I have not run this code, but it should work I think.

I like table layouts for forms, and not the stupid list stuff that I have serious problem to make cross-browser css for. So I went of and found some examples.

Now:

One problem with decorators is that I don’t understand its syntax, or how to build them.


$this->clearDecorators();
$this->setDecorators(array('Form'));

As a beginning, I did this - and only got the form tag. Ok, nice.


$this->clearDecorators();
$this->setDecorators(array('FormElements', 'Form'));

Now, I got the form tag, and inside it - all the content presented as list.

So far so good.


$this->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'table')),
'Form'	
));

It seem to be an pattern that we add stuff backwards.
First we add the formelement, which iterate through each element and ask it do “decorate” itself - which print the input tag in default list.
Then we add an htmltag something, that takes all content from formelements and stuff it in a tag of our choise… which is table.
Finaly, everything is stuffed in form tag…

nice…
Where is the documentation for how to use/config htmltag?
Let me explain better:


// typical 2 column elements with label + input
$element->setDecorators(array(
    'ViewHelper',
    'Errors',
    array(array('data' => 'HtmlTag'), array('tag' => 'td', 'class' => 'element')),
    array('Label', array('tag' => 'td'),
    array(array('row' => 'HtmlTag'), array('tag' => 'tr')),
));

// for buttons where left td should be empty
$element->setDecorators(array(
    'ViewHelper',
    array(array('data' => 'HtmlTag'), array('tag' => 'td', 'class' => 'element')),
    array(array('label' => 'HtmlTag'), array('tag' => 'td', 'placement' => 'prepend')),
    array(array('row' => 'HtmlTag'), array('tag' => 'tr')),
));

// for the form
$form->setDecorators(array(
    'FormElements',
    array('HtmlTag', array('tag' => 'table')),
    'Form',
));

Here it uses an “array notation” or whatever. But it doesn’t really tell me anything.
ViewHelper prints the input tag. Then add error. Stuff that into… uh…

array(array(‘data’ => ‘HtmlTag’), array(‘tag’ => ‘td’, ‘class’ => ‘element’))
???

If someone could explain in better detail what and how those arrays works, it would be nice.

Since its just an array of arrays of arrays … could this be written in a non-array way? Maybe that would tell me how this is put together and what objects are involved.

There is no need to use clearDecorators() and then setDecorators(). The way that setDecorators is implemented calls clearDecorators and then calls addDecorators.

How I use Decorators.

Within Zend Form, there are 4 object groups to which decorators can be applied, Forms, Sub Forms, Display Groups and Form Elements. Certain decorators work on some, but won’t work on others. For instance Zend_Form_Decorator_Errors will not work on Display Groups because Display Groups do not have errors, which is some what of a flaw in its design. I’m going to break this into a series of posts starting with elements and how to apply decorators to them just cause it helps me think more clearly >.>

To apply a decorator to an element:


$element = new Zend_Form_Element_Text('blah');
$element->setLabel('Blah Field')
          ->clearDecorators()
          ->addDecorator('ViewHelper')
          ->addDecorator('Label')
          ->addDecorator('Errors')
          ->addDecorator('HtmlTag', array('tag'=>'div', 'class'=>'newClass', 'id'=>'newId');

This creates anew text field named blah, sets the label removes the default decorators from the object and apply my decorators, ViewHelper which will element to use formText to render the input tag, Label to render the label, errors to render the errors and finally an div tag to act as a wrapper around them. Note how I placed attributes in the array. These will render as id=“newId” and class=“newClass”.

To apply a decorator to an Display Group:


// Fields that have already been added to the form
$elements = array('field1', 'field2', 'field3');

// Create a display group within a form
$this->addDisplayGroup($elements, 'group');

// Retrive the display group to apply decorators
$this->getDisplayGroup('group')
     ->setLegend('Grouped')
     ->clearDecorators()
     ->addDecorator('FormElements')
     ->addDecorator('Fieldset', array('class'=>'newClass'));

as i suspected.
Very nice example.

may I follow up?
addDecorator(‘HtmlTag’, array(‘tag’=>‘div’, ‘class’=>‘newClass’, ‘id’=>‘newId’);

This is quite clear, so no fuzz here.

array(‘Label’, array(‘tag’ => ‘td’)
array(array(‘label’ => ‘HtmlTag’), array(‘tag’ => ‘td’, ‘placement’ => ‘prepend’))

arrays like these are among those who confuse me most.
First one is Label decorator, and it seem we can “configure” it to put TD around the label tag.
Second is… i guess it tells to make an <td></td> … but thats a guess.

From the decorator for “buttons”:
array(array(‘data’ => ‘HtmlTag’), array(‘tag’ => ‘td’, ‘class’ => ‘element’)),
array(array(‘label’ => ‘HtmlTag’), array(‘tag’ => ‘td’, ‘placement’ => ‘prepend’)),
array(array(‘row’ => ‘HtmlTag’), array(‘tag’ => ‘tr’))

Here we dont use premade decorator like Label, HtmlTag etc. It seems we use some code to mark out data, label and rows, and these are given an HtmlTag decorator??? And then the HtmlTag is configured in various way like the array behind shows…

Where do ppl find the doc for this? Because I have read the api doc at zend 100 times, and it doesn’t explain these type of “config”.

To apply a decorator to Sub Forms:


public function __construct()
{
    parent::__construct();

    // Create a new Form
    $subform = new Zend_Form();
    $subform->addElement('text', 'field1')
            ->addElement('text', 'field2')
            ->addElement('text', 'field3')
            ->setLegend('My SubForm');
    // Add Sub Form
    $this->addSubForm($subform, 'subform');

    // Apply Decorators
    $this->getSubForm('subform')
         ->clearDecorators()
         ->addDecorator('FormElements')
         ->addDecorator('Fieldset')
         ->addDecorator('Errors');
}

There are several other decorators that are very useful. Probably my most prefered being Zend_Form_Decorator_ViewScript which allows you to render a form object according to your specifications.

Usage

First, create a file in your view/script directory called _element.phtml


<div class="<?= $this->class ?>">
    <?= $this->formLabel($this->element->getName(),
                         $this->element->getLabel()) ?>
    <?= $this->{$this->element->helper}(
        $this->element->getName(),
        $this->element->getValue(),
        $this->element->getAttribs()
    ) ?>
    <?= $this->formErrors($this->element->getMessages()) ?>
    <div class="hint"><?= $this->element->getDescription() ?></div>
</div>

Now for your form


public function __construct()
{
    parent::__construct();
    $viewScript = Zend_Form_Decorator_ViewScript();
    $viewScript->setViewScript('_element.phtml');
    
    $element = new Zend_Form_Element('Text');
    $element->setLabel('blah')
            ->clearDecorators()
            ->addDecorator($viewScript);
    $this->addElement($element);
}

This function call,


    <?= $this->{$this->element->helper}(
        $this->element->getName(),
        $this->element->getValue(),
        $this->element->getAttribs()
    ) ?>

is some what confusing. Every element has a variable called helper which is passed along with it, this tells Zend_Form what helper is to be applied while rendering it. Every Zend_Form element has a parameter chain as such: (string|array $name, [mixed $value = null], [array $attribs = null]).

Where do ppl find the doc for this? Because I have read the api doc at zend 100 times, and it doesn’t explain these type of “config”.

You have to read the code to understand how it works unfortunately. There aren’t many books or guides on Zend_Form as of yet. I don’t suspect they have found wide usage among many developers. Most people still cling to the idea that forms should be handed in the view and create there own form processors.

Ok so I did some reading. When your using setDecorators or addDecorators, there is a couple things that will help you.

  1. It is HtmlTag’s natural disposition to wrap everything that is before it.
  2. Only each decorator should be of the form array(Deocrator, Options). From your example array(‘data’ => ‘HtmlTag’) becomes your decorator and array(‘tag’ => ‘td’, ‘class’ => ‘element’) become the decorator’s options.

I already do viewscripts sort of:


<form action="<?php echo $this->escape($this->searchForm->getAction()); ?>"
      method="<?php echo $this->escape($this->searchForm->getMethod()); ?>"
      id="<?php echo $this->escape($this->searchForm->getId()); ?>">

      <?php echo $this->searchForm->getElement('search')->getDecorator('ViewHelper')->render(null); ?>
      <?php echo $this->searchForm->getElement('submit')->getDecorator('ViewHelper')->render(null); ?>

</form>

This is ok for simple stuff.
It becomes tedious when you have random number of elements. Like query db, and get 1-10 rows of stuff that is to be presented in a form.

Then you start add lots of code in view like if/else do/while - it becomes ugly. I work with java most of time, so I painfully know.

Java servlets was once like that. The view code was full of "escape to java, do if/while substr/compare, then escape back to html mode. Simple forms could become extremely ugly. So also with php. The html get lost in code.

Thats why they invented JSP, which is a kinda “smarty” for java. I hate it. Why the f¤%& do you want to do programming in arbitrary languages like jsp tags, struts tags, ognl, el, and a few other ****** languages from x number of xml template engines.
Well - sry, thats my rant on java web programming. I know jack about .net but it has to be better than java.

The reason for why Zend made decorators for forms is that: In JSP, or plain viewscript, or vanilla php, you end up doing the same form over and over again with its flaws and lack of functionality. You make it once, then copy/paste it around with small adaptions. And thats not really good…

For complex forms - sure. But most of my forms is simple and can be automated. Thats why I try understand decorators. But almost all tutorials skip the explaining of “how to configure each element” and just provide finish decorators… which usually don’t fit your world, and nobody understand.

Somehow I still think pear quickform mixed with pear htmltable, was a better option. But its long time since I used that one.

Anyway - thx for info. It was helpful.

Deo crator? hehe

“array(Deocrator, Options)”
I understand.

Hmm. If someone got the time to do a tutorial on that part, maybe that could shed a better light on stuff. I think that part is the hardest for ppl to understand, and in the end - make it easy for ppl to adapt.

nice

Sorry, I don’t spell check often >.>…

Ok, little more info. array(‘Label’, array(‘tag’ => ‘td’,‘placement’ => ‘prepend’)) will create a Label tag placed before an element. array(‘tag’=>‘td’) sets the option of Zend_Form_Decorator_Label. Zend_Form_Decorator_Label calls Zend_Veiw_Helper_FormLabel which renders an html label of the form <label atrributes>Blah</label>. If the tag option is set, then it will wrap it make use of Zend_Form_Decorator_HtmlTag to create an HtmlTag to wrap the label.

I actually prefer to use Zend_Form_Decorator_ViewScripts as often as I can. There have been several times where standard decorators just don’t fit the way that I want them to. Using Zend_Form_Decorator_ViewScripts allow you to have the same functionality of a regular Decorator but with the ability to customize the view according to your liking. I usually create an _element.phtml decorator that handles the display of all my decorators then apply a few custom ones to suite my liking.