I’m often asked what my favorite component of Zend Framework is, and I invariably answer: “Forms”. Forms have always played an awkward role in the model-view-controller paradigm. Sure, the form is just HTML, but to me, it represents something more abstract than that. It represents the HTML form itself, taking user input, normalizing and validating it, and also being able to show the form again when errors occur. This can take quite a large amount of code.
If you’re able to automate all of that stuff, then all you are really left with is configuration. Each form consists of elements, and each element has several attributes which modify the overall functionality of the form. Let’s look at some of these aspects.
Defining a Form
A form itself is an instance of Zend_Form. There are many ways to define one. I prefer to extend Zend_Form for each of my forms, and define them inside the class itself. Each form contains a few important fields (action, method) and also it contains at least one element. Let’s look at how we define an element. Each element probably has a few common attributes: a field type, a label, an element name, and a description. Let’s represent this element in ZF. Note, there are several different ways to represent forms. They can literally be done via configuration, or by a few different PHP methods. This is my favorite.
$username = new Zend_Form_Element_Text('username', array(
'label' => 'Username',
'description' => 'This is a description'
));
|
In the above code, you can see we define these three things. There are also some other important attributes for elements. Let’s add a few more, and show a few fields at once.
$this->addElements(array(
new Zend_Form_Element_Text('username', array(
'label' => 'Username',
'required' => true
)),
));
|
Filters and Validators
One very helpful feature with the forms component is the ability to chain filters and validators. A filter is something that automatically manipulates incoming data to your desired format, and a validator is something that is checked when processing the form. This greatly simplifies your controller code, and your model code, because you are shifting all of this logic into a much more organized structure.
For example, if you are working with a username element on a registration form, then you may want check that the user does not already exist, the username does not contain any illegal characters, and the username is within a desired length.
If you had an element where the user can enter a few paragraphs about himself, then you would want to apply some filters to make sure the data is clean. You may want to add things like StripTags to remove any HTML, StringTrim to remove any extra whitespace, etc. You could also apply some custom BBCode parsing filters if you wanted. You could create your own filter for this, or use the Callback filter which you can use for everything else.
The idea is to do as much validation as possible so you know that your data is 100% ready for your application. If you have a VARCHAR(255) field, then limit your string to 255 characters so nothing gets truncated. Be very strict.
Filters are more of a time saver to me… but they are nice because they help keep all the data very consistent or can also be part of your cleaning process. If you are allowing user input, then remove any HTML is necessary. You get the idea.
Here is a more complete example of a registration form definition:
$this->addElements(array(
new Zend_Form_Element_Text('username', array(
'label' => 'Username',
'required' => true,
'validators' => array(
array('StringLength', false, array(4, 16)),
array('Alnum'),
array('Db_NoRecordExists', false, array('users', 'username'))
)
)),
new Zend_Form_Element_Text('email', array(
'label' => 'Email Address',
'required' => true,
'validators' => array(
array('EmailAddress'),
array('Db_NoRecordExists', false, array('users', 'email'))
)
)),
new Zend_Form_Element_Password('password', array(
'label' => 'Password',
'required' => true
))
));
|
Skinny Controller, Fat Model
With a form containing the fields above, we can really simplify our controller code. To me, controller’s should contain the flow of an application, without really performing any of the work. Here is an example of a registration action.
if ($this->_request->isPost()) {
if ($form->isValid($this->_request->getPost())) {
$user->register($form->getValues());
$this->redirector->goToUrl('/welcome');
}
}
$this->view->form = $form;
|
If the user has submitted (posted) any information, then the form validates that posted information. If it’s valid, it will proceed to pass the data array to our model. At this point, all of our filters and validators have been executed, so we know exactly how the data is formatted, and that it’s safe to use. We can proceed to redirect the user away, or perform any other flow logic we need to at this point.
We also pass the form to the view, which is crucial. On a regular page view, the form will be loaded normally. On a failed submission, then it will show the form with all of the error messages, and the posted data populated back into the form elements.
View Scripts & Decorators
Here is all the code we need in our view to show a form. The rest can be done via CSS, or custom decorators if we need it.
By default, Zend_Form outputs a pretty usable chunk of HTML. I have no problems styling it via CSS which is really great for our separation of concerns. However, there will be times when you need markup in certain format, and Zend can handle this with decorators. They are a little tricky to learn, so I will save that for another blog post. There should be sufficient documentation on their website.
Dealing with Models
As per our controller code, the model would receive $form->getValues(), which would be a pre-validated and pre-filtered array of our data. Here is an example:
// ...
public function register(array $user) {
}
// ...
|
To me, this is beautiful. We are working with a very basic interface, and do not have to manually specify every value that we pass or expect. While this is personal taste, I cringe whenever I see people pass 5-10 values to a model. Their controllers all end up being huge. The code is also not dependent on our form, because it’s just dealing with an array of data. It makes calling our models very simple, too. The model is where the code should start to get complicated. From this point, we can use our database adapter object, or deal with several other libraries.
Summary
The form component is one of the larger (largest?) components in Zend. Don’t be afraid to ask for help on the different forums, or ask below and I can point you to a good resource. Like I said above, this is the most useful component in Zend to me, so if you are considering taking the time to learn how to use it, I strongly advise to do so.
You may also be interested in caching forms.