Sunday, 1 May 2011

Zend Form Decorators

I’d felt a lot of pain when I first used Zend_Form. The reason was that I wasn’t able to layout form elements the way I wanted them to be. Zend was rendering elements in dt and dd whilst I was looking forward to place them in row and cells.
In short I wasn’t aware of Zend_Form decorators in the beginning and when I studied them for some time I felt uncomfortable using them.
So I decided to use html forms in my phtml files-template file. Though by doing so I achieved the desired layout however I missed entire facilities/functionalities that Zend_Form component provide us such as validation, filtering, dispaying error messages and so on.
When I first created form with Zend_Form component I was served with layout like the following.


Label on the top of the text/ password element.

This is because Zend Framework render form label in "dt" tag and text/password form element in "dt","dd" tag and put entire form element in "dl" tag by default.

Code rendered was 



<form method="post" action="">
<dl>
<dt><label for="agencyName">User Name:</label></dt>
<dd>
<input type="text" name="agencyName" id="agencyName" value="">
</dd>
<dt><label for="advertiserName">Password</label></dt>
<dd>
<input type="text" name="advertiserName" id="advertiserName" value="">
</dd>
<dt>&nbsp;</dt>
<dd>
<input type="submit" name="submit" id="submit" value="Login">
</dd>
<dl>
</form>


This was not the layout I was looking for. I was looking for 
layout like the following. 

 

To create form as above one need to put label and individual form element in "td" tag, put that "td" tag in "tr" tag and put all these in "table" tag. E.g 



<form action="" method="post">
<table>
<tr>
<td><label for="username">User Name:</label></td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td><label for="password">Password</label></td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" name="submit" id="submit" value="Login"></td>
</tr>
</table>
</form>

Zend form decorators can be used to create above form.

If you are beginner or have little experience working with zend, you may be facing difficulties in rendering Form using Zend_Form the way you want.

In this post I am providing indepth and comprehensive study of how to use Zend_Form_Decorators.

So let’s get started.

Although in the beginning you may find it difficult to use Zend_Form decorators, however once you get used to it and understand the basics, you will find these quite easy, helpful and interesting.

To create the login form in the figure 2. you will need the following code 



class forms_LoginForm extends Zend_Form
{

public function __construct($option=null)
{

parent::__construct($option);

$this->setMethod('post');

$username=$this->CreateElement('text','username')

->setLabel('User Name:');

$username->setDecorators(array(

'ViewHelper',
'Description',
'Errors',
array(array('data'=>'HtmlTag'), array('tag' => 'td')),
array('Label', array('tag' => 'td')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr'))

));

$password=$this->CreateElement('text','password')

->setLabel('Password');

$password->setDecorators(array(

'ViewHelper',
'Description',
'Errors',
array(array('data'=>'HtmlTag'), array('tag' => 'td')),
array('Label', array('tag' => 'td')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr'))

));

$submit=$this->CreateElement('submit','submit')

->setLabel('Login');

$submit->setDecorators(array(

'ViewHelper',
'Description',
'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td',
'colspan'=>'2','align'=>'center')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr'))

));

$this->addElements(array(

$username,
$password,
$submit

));

$this->setDecorators(array(

'FormElements',
array(array('data'=>'HtmlTag'),array('tag'=>'table')),
'Form'

));

}
}



Explanation:

First we extend our form from Zend_From and define our constructor. It is necessary to call parent class constructor before defining anything in your own constructor-by doing so our form will inherit all the functionality of Zend_Form.

After calling parent constructor we set our form method by simply $this->setMethod(‘post’).

Next we create text element and set its label.

The lines after setting label of the form are important because these lines give new look to our form. The code is quite simple. We call setDecorators() method of the form to override the default decorators behavior of the form.
The code 



$username->setDecorators(array(

'ViewHelper',
'Description',
'Errors',
array(array('data'=>'HtmlTag'), array('tag' => 'td')),
array('Label', array('tag' => 'td')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr'))

));



is very simple. We are skipping “ViewHelper”, “Description” and “Errors” for now. We will explain it some other time. We can define our decorators for these things as well if we need in future. However leave it as these are for now.

After “Errors” we define array which tell Zend to override the default decorators of 



<input name="”username”" type="”text”">

. By doing so zend will wrap "input" tag in .

Next array define tag for label and put label in td tag as 



<td><label ..></td>

.

Next array define tag for the row of the form. This wrap 



<label ..>and <input>


in tr tag. The above code will create 



<tr>

<td>User Name:</td>

<td><input type=”text” name=”username”></td>

</tr>



However defining decorators for the submit button we have change the code a lit bit.

As label is appearing on the button so we don’t need to define decorator for the label. By leaving decorator for label undefined, it means zend will use its default decorator for the label of the submit button.

And as we want to put button at the center of the row, so we have defined 



array(array('data'=>'HtmlTag'), array('tag' => 'td',
'colspan'=>'2','align'=>'center')),



This will create html as 



<td colspan=”2” align=”center”><input type=”submit” ….></td>



Next we add elements to form as 



$this->addElements(array(

$username,
$password,
$submit

));



At the end we define decorators for the form-to wrap form elements in the html tag.

I have already said that zend wrap entire element in "dl" tag.

As we have wrap element in the "tg" and "tr" tag properly. So we need to wrap these "td" and "tr"’ in the "table" tag. To do so we have defined 



$this->setDecorators(array(

'FormElements',
array(array('data'=>'HtmlTag'),array('tag'=>'table')),
'Form'

));



This code will wrap all "tr" in "table" tag.

Although this form is simple and contain only two fields however you can create very large form by following this procedure.

Next I going to create a form that may help lot of souls. First see the following form.


You can clearly see we have put all the form elements in the single row.

The html of the above form is 



<table>
<tr>
<td>
<label for="username">User Name:</label>
</td>
<td><input type=”text” name=”username”></td>
<td>
<label for="password">Password</label>
</td>
<td><input type=”password” name=”password”></td>
<td align="center">
<input type="submit" name="submit" id="submit" value="Login"></td>
</tr>

</table>



You can clearly see that we have wrapped the elements in tags and then wrap those "td" tags in single "tr" tag and then "table" . To create the above form we will make very little changes in our code above.

The new code would be 



class forms_LoginForm extends Zend_Form
{

public function __construct($option=null)
{

parent::__construct($option);

$this->setMethod('post');

$username=$this->CreateElement('text','username')

->setLabel('User Name:');

$username->setDecorators(array(

'ViewHelper',
'Description',
'Errors',
array(array('data'=>'HtmlTag'), array('tag' => 'td')),
array('Label', array('tag' => 'td')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr', 'openOnly'=>true))

));

$password=$this->CreateElement('text','password')

->setLabel('Password');

$password->setDecorators(array(

'ViewHelper',
'Description',
'Errors',
array(array('data'=>'HtmlTag'), array('tag' => 'td')),
array('Label', array('tag' => 'td')),

));

$submit=$this->CreateElement('submit','submit')

->setLabel('Login');

$submit->setDecorators(array(

'ViewHelper',
'Description',
'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td',
'colspan'=>'2','align'=>'center')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr', 'closeOnly'=>'true'))

));

$this->addElements(array(

$username,
$password,
$submit

));

$this->setDecorators(array(

'FormElements',
array(array('data'=>'HtmlTag'),array('tag'=>'table')),
'Form'

));

}
}


You can see we done very minor changes in our form.

In the first element “username” setDecorators() we have changed our array as 



$username->setDecorators(array(

'ViewHelper',
'Description',
'Errors',
array(array('data'=>'HtmlTag'), array('tag' => 'td')),
array('Label', array('tag' => 'td')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr', 'openOnly'=>true))

));



The only changes are in the last line of the array. We have now set ‘openOnly’ => true for the “row”. It means that we are wrapping elements of username in , however we are putting but opening it only- tag would not be closed when Zend will render this element.

Next 



$password->setDecorators(array(

'ViewHelper',
'Description',
'Errors',
array(array('data'=>'HtmlTag'), array('tag' => 'td')),
array('Label', array('tag' => 'td')),

));


Here we have defined only two arrays -third one is missing. The reason is we are wrapping elements in the "td" tag putting nothing for the row.

At the final change are made in the submit element setDecorators() method call as 



$submit->setDecorators(array(

'ViewHelper',
'Description',
'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td',
'colspan'=>'2','align'=>'center')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr', 'closeOnly'=>'true'))

));


Here also we have made changes in the last line-array(array(‘row’)…..). we have defined ‘closeOnly’ => ‘true’. This will close the tag opened in the first element setDecorators() section.

The rest of the code is same.
The rest of the code is same.

In almost all the websites where you find form will compulsory field indicate those fields either by placing text or image next to that field.

Zend_Form setDescription() can help you out and give you an easy way to achieve this goal. Look at the following form.


In the form above we have placed image next to our username field to tell the user that username is compulsory field.

To achieve this you will need to do very little changes in your previous form.

Code to create the above form is 



class forms_LoginForm extends Zend_Form
{

public function __construct($option=null)
{

parent::__construct($option);

$this->setMethod('post');

$email = $this->CreateElement('text','username')

->setLabel('User Name:')

->setDescription('path/to/image');

$email->setDecorators(array(

'ViewHelper',
array('Description',array('tag'=>'','escape'=>false)),
'Errors',
array(array('data'=>'HtmlTag'), array('tag' => 'td')),
array('Label', array('tag' => 'td')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr'))

));

$password=$this->CreateElement('text','password')

->setLabel('Password')
->setDescription('path/to/image');

$password->setDecorators(array(

'ViewHelper',
array('Description',array('tag'=>'','escape'=>false)),
'Errors',
array(array('data'=>'HtmlTag'), array('tag' => 'td')),
array('Label', array('tag' => 'td')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr'))

));

$submit=$this->CreateElement('submit','submit')

->setLabel('Login');

$submit->setDecorators(array(

'ViewHelper',
'Description',
'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td',
'colspan'=>'2','align'=>'center')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr'))

));

$this->addElements(array(

$username,
$password,
$submit

));

$this->setDecorators(array(

'FormElements',

array(array('data'=>'HtmlTag'),array('tag'=>'table')),

'Form'

));

}
}


If you compare the first form and this form code, you will see we have done very minor changes. If you compare this for with the first one, you will easily find the changes. However I will define these change for the beginners.

First we have added setDescription() method to our email and password elements of the form. This will add image as a description, however by default Zend will render description at the next row of the email as well as password fields. To put these images will need to define decorators for it. The only changes we will need to do in the setDecorators() methods are
Array(‘Description’,array(‘tag’=>’’,’escape’=>false)) at the place of “Description”. Look at the following code. 



$email->setDecorators(array(

'ViewHelper',
array('Description',array('tag'=>'','escape'=>false)),
'Errors',
array(array('data'=>'HtmlTag'), array('tag' => 'td')),
array('Label', array('tag' => 'td')),
array(array('row'=>'HtmlTag'),array('tag'=>'tr'))

));


you can clearly see these changes in line 3. we have defined decorators for the description of the form element.

No comments:

Post a Comment