Lifting the Skirt on Zend Framework 1.5 - Zend_Form
With the preview release of Zend Framework 1.5 now out and things looking good for a General release in late February, let's take a moment to really see what is new. This will be an ongoing series for a while to help you get an idea of what's coming down the pipe.
Zend Form
Many developers like the model of being able to programmatically create all elements on a page. An equal number like to code the HTML separately. If you are one of the ones that like to create things in code, you are going to love Zend_Form. Zend_Form gives you all the tools necessary to create forms and form elements via PHP code. Like everything in Zend Framework, almost everything is configurable; however the defaults are probably good for most uses. Zend_Form helps you by simplifying the creation of these form elements as well as adding programmatic controls for validation, ordering, filtering, and grouping. The Zend Framework documentation contains a great Quick-Start Guide written by the component author, Matthew Weier O'Phinney that gives you the basics. To show you just how simple this is to use, here's a quick code snippet.
<?php
$form = new Zend_Form;
$form->setAction('/resource/process')
->setMethod('post')
->setAttrib('id', 'login');
$username = new Zend_Form_Element_Text('username');
$username->addValidator('alnum')
->addValidator('regex', false, array('/^[a-z]/i'))
->setRequired(true)
->addFilter('StringToLower');
$form->addElement($username);
$password = new Zend_Form_Element_Password('password');
$password->addValidator('stringLength', false, array(6))
->setRequired(true)
$form->addElement($password);
$form->addElement(new Zend_Form_Element_Submit());
You can begin to get a feel for how this works from that snippet and how simple it is to build not only simple but really complex forms. I've listed the verbose version above so that you can see the granularity of control you have; here is the sparse version.
<?php
$form = new Zend_Form(array(
'action' => '/user/login',
'method' => 'post',
'elements' => array(
'username' => array('text', array(
'validators' => array(
'alnum',
array('regex', false, array('/^[a-z]/i')),
array('stringLength', false, array(6, 20))
),
'required' => true,
'filters' => array('StringToLower')
)),
'password' => array('password', array(
'validators' => array(
array('stringLength', false, array(6))
),
'required' => true,
)),
'submit' => 'submit',
),
));
You will notice that in this version everything is defined in an array and just handed to Zend_Form. If you work with Zend Framework at all you know that if it's an array, it can most likely be a config. Zend_Form is no exception. It can take a Zend_Config as a parameter instead of an array. This allows you to define your form in an INI file. If we were creating our login form via a Zend_Config, the INI would look like this.
[development]
; general form metainformation
user.login.action = "/user/login"
user.login.method = "post"
; username element
user.login.elements.username.type = "text"
user.login.elements.username.options.validators.alnum.validator = "alnum"
user.login.elements.username.options.validators.regex.validator = "regex"
user.login.elements.username.options.validators.regex.options.pattern = "/^[a-z]/i"
user.login.elements.username.options.validators.strlen.validator = "StringLength"
user.login.elements.username.options.validators.strlen.options.min = "6"
user.login.elements.username.options.validators.strlen.options.max = "20"
user.login.elements.username.options.required = true
user.login.elements.username.options.filters.lower.filter = "StringToLower"
; password element
user.login.elements.password.type = "password"
user.login.elements.password.options.validators.strlen.validator = "StringLength"
user.login.elements.password.options.validators.strlen.options.min = "6"
user.login.elements.password.options.required = true
; submit element
user.login.elements.submit.type = "submit"
To implement this you simply instantiate your Zend_Config() and then your form:
$config = new Zend_Config_Ini('/path/to/config.ini', 'development')
$form = new Zend_Form($config->user->login);
You can see the complete example, including a controller that uses it not only to display the form but also to validate the results, on the documentation page.
As I said before, some developers prefer to develop like this and others don't. Those who prefer this method will rejoice with singing and dancing because this is a very nice implementation. It is flexible enough to be used for complex forms but the syntax is simple to grasp and won't get in the way when building simple forms.
Those who prefer to code the HTML directly have already stopped reading by now.

Comments
I think I'm going to go rejoice in the streets now, singing and dancing.
God bless you!!
Is there an easy way to add a fieldset?
@dinaboff: You can create a DisplayGroup to group elements; by default, DisplayGroups are rendered in fieldsets. To create a DisplayGroup, use the addDisplayGroup() method, and pass it an array of elements to group; you also need to pass it a name, and, optionally, some configuration options:
$form->addDisplayGroup( array('login', 'password'), 'loginDetails', array('legend' => 'Login Details') );Read the manual -- there's plenty of information on Zend_Form already documented.
@sicouk: The benefit of storing a form configuration in a config file is simply for creating a re-usable form (and that's only one way to create re-usable forms; you can also subclass).
Many people like to use configuration files in their applications. It allows them to perform a poor-man's dependency injection, and also allows less-savvy developers to modify portions of the application without needing to touch the code. Regardless, using a config file is optional; Cal merely presents it as one available option for working with Zend_Form.
And, in case you weren't aware, one way you can create a Zend_Config is from a PHP file that returns an array:
You could also simply pass the array directly to Zend_Form's constructor; it can utilize either an array or a Zend_Config object.
Forms are a part of the display logic - Views - and IMO they should be constructed in Views rather than make a mess in the Controllers.
But then may be I am from a different world ;)
@Steve Smith: the config files go wherever you normally store your config files, and you would load them accordingly. Additionally, you don't need to use configs at all -- for your first experiments, try just programattically creating the form and element objects.
@tariquesani: The controller actions are not dependent on a form, controllers simply utilize forms as they would any other objects. But you have a point regarding mixing display logic with controllers; however, ultimately, it's moot, as I'll explain below.
If you look at the goals for Zend_Form, they include:
Forms are not just display logic. When creating a typical form, you're thinking about how to validate the fields submitted and filter the submitted data. Zend_Filter_Input can handle this end easily.
However, the other part of the equation, rendering, is display logic, and that has a whole additional set of issues: how do you inject submitted values into the form input when re-displaying a failed form submission? How do you place the error messages? How do you i18n-ize labels and error messages?
If you create your HTML markup by hand, you're now having to juggle a ton of view variables -- or at the minimum a large array of items. And where does this information come from -- how is it built? Creating well-crafted forms is a time-consuming and repetitive task.
Now, you'll probably be happy to find that the actual rendering logic for Zend_Form is actually separated into a different set of classes. To a large extent, Zend_Form is its own mini-MVC layer, separating the tasks of validation, filtering, and rendering (or, to use Zend_Form terms, decoration) into discrete domains. Rendering is taken care of via Decorators, and you can customize the display logic any way you want with these.
And, to be honest, you can also simply omit them, and send your form object to your view script, and use the elements it contains to help build your markup by hand. Best of all worlds. :-)
Great work guys.
<?xml version="1.0" encoding="ISO-8859-1" ?>
<form name="f_listing" datasource="tr_listing">
<input type="hidden" name="listing_seen" value="1"/>
<input type="hidden" name="is_service" value="0"/>
<input type="hidden" name="id" identity="true"/>
<input type="hidden" name="client_id"/>
<input type="text" name="ref_id" label="Ref Id" required="true"/>
<input type="hidden" name="status"/>
<input type="checkbox" name="featured" label="Featured"/>
<input type="hidden" name="rent_sale" value="2" required="true">
<option value="1" name="For Rent"/>
<option value="2" name="For Sale"/>
</input>
<input type="list" name="type_id" label="Property Type" required="true">
<query value="SELECT id,value FROM tr_listing_type WHERE is_service = 0"/>
</input>
</form>
IMO this is much more compact way.
How to place all inside a MVC, whish part would be on the controller, and on the view using there rendered html form or using the array for templates?
I have been using Zend_Filter_Input for custom chaining validation but I will switch to Zend_Form.
Aren't this 2 components (Zend_Form and Zend_Filter_Input) stepping on eachouther toes right now? Looks like it, since Zend_Form is not using Filter_Input for it's chaining validation.
@deusx: I see Zend_Filter_Input and Zend_Form as serving different purposes. Zend_Form does not use Zend_Filter_Input internally as validation and filter chains are done per element; Zend_Filter_Input is geared towards collections. What this means is that with Zend_Form, you can validate a single element at a time if you want, or even partial sets of data (for instance, in AJAX requests) - something that Zend_Filter_Input is not really geared for. This feature of Zend_Form also allows you to re-use individual elements across multiple forms, another trick that would be difficult for Zend_Filter_Input. However, note that both use Zend_Validate and Zend_Filter for their chains.
Additionally, I see the two as responsible for separate arenas. Zend_Filter_Input is a great choice for use in your model classes, as it allows you to validate data passed to your model and filter it for the model storage mechanism. This is particularly useful when writing models that you then attach to servers such as Zend_XmlRpc_Server and Zend_Rest_Server.
Zend_Form is geared more towards application logic -- i.e., gathering input to provide to models, as well as collecting items that should be displayed to the user for action. Items in Zend_Filter_Input may never be displayed, but may come from other sources.
Finally, Zend_Form implements something Zend_Filter_Input does not do currently: it implements a plugin architecture to allow easily and simply specifying validators and filters, and providing for replacements of standard components.
@chelala: I personally subclass my forms, and then all I need to do is the following in my controller actions:
$request = $this->getRequest(); $form = new My_Form_Login(); if (!$request->isPost()) { $this->view->form = $form; // renders view script return; } if (!$form->isValid($request->getPost())) { $this->view->form = $form; // renders view script; form now has values and error messages return; } // success! $values = $form->getValues(); $model->save($values); $this->_redirect(...);In your view script, to render the form, you simply do:
Basically, the MVC usage does not differ much from the standard procedural usage.
Ty.
Zend form is very nice, and for me is an end-all formclass, especially with the translation that make multi-lingual sites very easy to manage. I use Zend_Translate with po-files edited on "pootle" translation manager. Highly recommended!