Creating forms in Zend Framework (ZF) is easy, but creating and managing complex forms can get messy! Using config files is perfectly good if you use standard Zend Form elements; it is quick and easy to setup a form in a few minutes however you will have limited control over how the form is rendered. If you want to display your own custom form element correctly in Zend Framework then some configuration is needed in order to achieve this with a config file. An alternative approach is to create some custom objects that extend the base Zend Form object – you can still use the config but also add in your own custom Zend Form elements.

If you use a custom form object then all of your form logic can be grouped together. The object can include data validation with Zend validator (and/or normal PHP validation for more complex validation routines), form data retrieval, form view format and element view rendering. This has the combined benefit of freeing the controller of any form logic needed for the custom objects and ensures the “skinny controller, fat model” approach can be used, avoiding duplicating logic as each form object is now responsible for its own data.

This article will show you how to create custom form element objects which change the standard view template for an element – the code from all the examples is available on github too, if you want to follow along. The approach shown here enables us to add logic into the element, so that the element behaves the same way in any form. A good example could be a select box that can pre-fill data from a database table and automatically add a Zend Validator to the array. We’ll also cover how you can have total control over the way the form is rendered, by using a view script. As an example, we’ll create an e-commerce checkout where a billing address and delivery address contain exactly the same fields with JavaScript and Zend Validation; our postcode fields will trigger a postcode lookup.

Creating a Custom Zend Form Object

Extending the Zend Form the Right Way

To gain great functionality on top of ZF, the best way is to extend the framework, creating your own base, then extend that per project, as shown in the code example below. This way if you want to add a new method to an object then you just need to add it to your own main object, leaving the core Zend object unmodified. For instance if you want every Zend Fhttp://techportal.inviqa.com/wp-admin/post.php?post=3884&action=edit&message=6orm to have a getValidatedFormData() method then you can implement it in your base class (and should Zend implement the same functionality later on then you can remove your implementation, or leave it and override theirs). Hopefully this seems obvious but I am amazed how often I see code that uses only the core Zend objects.

class MyBase_Form extends Zend_Form
{
....
}

Your main base class doesn’t need to do any more than just extend the base Zend Form object; this makes it easier to update all your objects with new methods or override the core Zend methods should you need to do so in the future. The specific form (in this case Email_Form) should extend your base Zend Form object we just created, so that any and project specific-methods can be added leaving you to re-use your MyBase object.

This level of abstraction may seem slightly “over the top”, however it can save you a lot of time later if more changes are needed such as changing the default display from “dt” and “dd” tags to a table layout. With the abstraction layer, this only needs to be done once and all form objects will be converted. This approach has the added benefit of easier testing as you only need to test the functionality you are actually writing.

So far, all we have done is set up a form object, now we can configure the form’s elements and more importantly the validation. Using this approach we keep the validation logic together and, should the elements change, the validation can change to match. The changes are unit testable and re-useable, and validation will be also available if this form is to be used as a sub-form.

Adding the Elements

To add elements to this form, we could use a Zend_Config objects for adding standard elements only, but we get more control by adding the elements manually – so we’ll do that!

class MyProject_Form_Address extends MyBase_Form
{
protected $_action_url = '/[controller]/[action]/';

public function init()
{
parent::init();
$this->setAction($this->action_url)
->setAttrib(‘id’, ‘project_form’)
->setMethod(‘post’);

$billing = new Zend_Form_Element_Textarea(‘billing_address’, array(
‘label’ => ‘Billing Address:’,
));
$billing->setRequired();

$del_diff = new Zend_Form_Element_Checkbox(‘different_delivery_address’, array(
‘label’ => ‘Deliver to different address:’,
));

$delivery = new Zend_Form_Element_Textarea(‘delivery_address’, array(
‘label’ => ‘Delivery Address:’,
));

$submit = new Zend_Form_Element_Submit(‘submit’, array(
‘label’ => ‘Send:’,
));

$this->addElement($billing, ‘billing_address’)
->addElement($del_diff, ‘different_delivery_address’)
->addElement($delivery, ‘delivery_address’)
->addElement($submit, ‘submit’);
}
}

As you can see from the above example, we have used standard objects but we have added attributes to specific elements in the form; this can be further extended by using the decorators (something we will cover later in this tutorial).

Validating the Form

For validation what place is better to check whether this form is valid than the form itself?  If a field is required only when a certain condition is true, you could check this every time in your controller. Alternatively you could extend the isValid() functionality and use any properly-extended Zend_Validation_Abstract object on any of the form elements.


public function isValid($value, $context = null)
{
$valid = parent::isValid($value, $context);
if ($this->isDeliveryAddressDifferent()) {
$this->getElement('delivery_address')->setRequired();
return parent::isValid($value, $context);
}
return $valid;
}

As you can see the isValid function now checks to see if the form element is checked and then set’s the delivery address to be required and revalidates the form, this way the form is more dynamic.

Getting the Data from the Object

There is the standard way of getting data from the object by getting the raw data array by using the getValues(), however there is a better way: create custom “getters”. By using getters, you can add more logic if it is needed, but the biggest benefit is that it will make your code much more readable – and of course again unit testing will be easier.

As you can see this way of getting the inputted data is simple to do, and it will make your code much more extendable and maintainable.

Changing the Way the Form is Rendered

As with most of Zend Framework, you can use as much or little of the standard framework as you like. In particular let’s look at ways of changing how the form is rendered, beyond simply adding a stylesheet. Sometimes you need something more than the standard “dd” and “dt” output, perhaps to output as a table or have full control over the view template for a given form or sub-form. This can be done by adding to the init() method.

The full path for this script would be application/views/scripts/form/_form.phtml

Creating a Custom Form Element

Extending Zend_Form objects is good practice but there is very little that you can’t do with Zend_Config. However when you come to add your own custom elements, this approach will really pay dividends. So now I will show you how to extend the standard form elements to make your own, such as creating a select / radio / checkbox set of elements from a database resultset, or using a Javascript library to create a date time field with custom view helpers, adding standard Zend_Validtors that it has to pass before the element is valid. In this tutorial, I will be extending a text box to make a standard email textbox, and pulling data from a database to pre-populate a select box.

Extending the Zend Form Element Object

Take a look at the following example; we simply add two validators when we initialise the object:

Now we’ve done this, those validators are automatically added everywhere this element is used. This means that your form needs to do no more than this: when you create the form, just give the name of the element you created rather than the standard text field.

Connecting to a Database for Pre-populated Elements.

The example below might look a little more complicated, but we will approach it step by step. The hardest part is getting the data from the database, which shouldn’t be too tricky (you could refactor this so that the data is pulled from a data object however for this tutorial I will just use the standard Zend_Db_Adapter). Here we are creating a select element but, by changing the parent object, it could equally be a set of radio options form or even a set of checkboxes instead. N.B. There is a obvious SQL injection issue here, as you know you should validate ALL input, for simplicity I have not added this but I do recommend that you implement something on all SQL variables. Here’s the code example first, then we’ll step through each bit below:

First, take a look at the constructor. We try and make the element as flexible as possible and as we don’t know which columns, table or even database this element would be communicating with, we need a way of getting that from the calling object. As in all Zend_Form_Elements there already is a second parameter which does different things depending on element, so we will utilise this and append our details to this array parameter, specifically for the database. In this example I use a rudimentary array but you can always refactor this to take an object, for example.

As part of the initial object construction, we populate the Zend_Form_Element_Select option array from the database; this also adds the Zend_Validate_InArray to prevent undesired details being entered into the form.

Next we shall look at getting the data from the database. This method mostly constructs the valid SQL, which I assume you are familiar with. This method loops through all of the database rows, then converts from a multi-dimensional array into a single-dimension array with the key from the database as the index. The text will be displayed in the form that the element has rendered.

Creating a Custom View Helper

There are times when you would like to use a standard element but with a custom view, e.g. a postcode lookup with javascript validation, or a date field. I will show you a quick way of doing javascript validation that will accept any input and add an OK button next to the element. There are two files that need to be created: a form object which extends the standard element, and the view helper.

As you can see this object doesn’t need to do any more than extend the standard object and change the view helper.

Now lets create the view helper: It is important to notice that the filename is FormEmail.php and the method name is formEmail – this is taken from the $helper. So Form{$helper}.php and form{$helper}.

This example is fairly simple and you would refactor to move as much logic as you could into the CSS, and create a JS with a function call taking in the id from the form element (which should be unique).

As you can see, the approach outlined here makes your controllers very easy to maintain, but more importantly the objects that know the most about the input from the user now contain the validation and filter logic – and so it can be reused and extended easily. Check out the github repository which you can find here ZF1-CustomElements, this is a fully working example of the above code, which also includes the pre-filling of the select from a database table. Hopefully this has given you some ideas for creating more manageable forms in your ZF1 projects, do let me know in the comments what you think!