In my previous post Zend Framework, A First Look, I discussed the lacking nature of ZF’s documentation, in particular, with regards to Zend_Form. I have since then learned that this is partly due to the fact that Zend_Form is a relatively new component. However, I still wanted to make use of Zend_Form in my current project and decided to trudge through the learning curve of creating a simple custom login form in conjunction with Zend_Config; the end result being an easy to maintain, custom form and this guide. Hopefully, this guide will make it easier for anyone else looking to take advantage of this very cool feature.

Before I begin talking about how Zend_Form works, I think it is important to explain a bit about Zend_Config and how an INI file is translated into PHP. As the Zend_Config Documentation points out, it makes use of PHP native parse_ini_file, however, this only goes so far as reading in the contents of the file and turning it into a 1 dimensional associative array (2 if a section is provided). Zend_Config extends this behavior by taking the keys of each association and breaking them down further based upon the specified key separator (default is ‘.’). and turning the remainder into arrays. In the end, Zend_Config takes a series of declarations such as the following:

1
login.elements.username.type = "text"

And turns it into:

1
2
3
4
5
6
7
8
9
10
11
12
13
Array
(
[login] => Array
(
[elements] => Array
(
[username] => Array
(
[type] => text
)
)
)
)

Now, back to Zend_Form. I think my biggest gripe with the documentation provided for Zend_Form is that it does not explain how anything is working behind the scenes. For instance, it does not explain that Zend_Form normalizes config statements by prepending ‘set’ when making calls to member methods. This is invaluable as it explains why

1
$form->setElementDecorators(array('ViewHelper'));

in PHP becomes

1
elementDecorators.helper = "ViewHelper"

in a Zend_Config_Ini. Internally, elementDecorators tells Zend_Form to call setElementDecorators(); This small example also shows why it is important to know how Zend_Config_Ini actually translates an INI to PHP…

1
2
3
4
5
6
7
Array
(
[elementDecorators] => Array
(
[helper] => ViewHelper
)
)

As we can see this declares an array of decorators to be passed into setElementDecorators(); The keys, in this case “helper”, do not necessarily matter as it is only needed to index the array, the values are what matter, as that is what is passed and used within Zend_Form. Similarly, it is important to know that almost all aspects of form and form elements, excluding form element type, are implemented internally as options. The following is an example INI file with comments explaining particular sections.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
; A basic Form config
; First level attributes are automatically treated as options.
action = "login/submit"
method = "post"
id = "login"
; Form Decorators
; Again these decorators are treated as options of the main form.
decorators.elements.decorator = "FormElements"
decorators.table.decorator = "HtmlTag"
decorators.table.options.tag = "table"
decorators.form.decorator = "Form"
; Username Element
; Form Element Type is specified explicityly, not as an option.
elements.username.type = "text"
; Username attributes (Label, Required, and validators are all
; declared as options).
elements.username.options.label = "Username:"
elements.username.options.required = true
elements.username.options.validators.alnum.validator = "alnum"
elements.username.options.validators.regex.validator = "regex"
; Validator parameters such as pattern to regex are also
; declared as options. I have not looked into it, but I
; assume this is due to the way Zend_Validate handles its
; own constructors.
elements.username.options.validators.regex.options.pattern = "/^[a-z]/i"
elements.username.options.validators.strlen.validator = "StringLength"
elements.username.options.validators.strlen.options.min = "5"

Bringing it all together, the following is the first version of my login form.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[login]
; General Form Information
login.action = "login/submit"
login.method = "post"
login.id = "login"
; Form Decorators
login.decorators.elements.decorator = "FormElements"
login.decorators.table.decorator = "HtmlTag"
login.decorators.table.options.tag = "table"
login.decorators.form.decorator = "Form"
; Username Element
login.elements.username.type = "text"
login.elements.username.options.label = "Username:"
login.elements.username.options.required = true
login.elements.username.options.validators.alnum.validator = "alnum"
login.elements.username.options.validators.regex.validator = "regex"
login.elements.username.options.validators.regex.options.pattern = "/^[a-z]/i"
login.elements.username.options.validators.strlen.validator = "StringLength"
login.elements.username.options.validators.strlen.options.min = "5"
; Password Element
login.elements.password.type = "password"
login.elements.password.options.label = "Password:"
login.elements.password.options.required = true
login.elements.password.options.validators.strlen.validator = "StringLength"
login.elements.password.options.validators.strlen.options.min = "6"
; Submit Form Element
login.elements.submit.type = "submit"
login.elements.submit.options.label = "Submit"
login.elementDecorators.viewHelper = "ViewHelper"
login.elementDecorators.errors = "Errors"
login.elementDecorators.tableData.decorator.td = "HtmlTag"
login.elementDecorators.tableData.options.tag = "td"
login.elementDecorators.tableData.options.class = "test2"
login.elementDecorators.label.decorator = "Label"
login.elementDecorators.label.options.tag = "td"
login.elementDecorators.tableRow.decorator.tr = "HtmlTag"
login.elementDecorators.tableRow.options.tag = "tr"

This creates a login form with a username and password field along with a submit button. However, instead of being wrapped in a definition list using definition titles for labels and definition data for input fields, I have wrapped the labels and input fields within a table similar to the google login form. Notice though, that I have sectioned this form using [login]. This is so that I can define all my necessary forms in one forms.ini file and load each form by section. I have also prepended each declaration with login. as a way to keep my form definitions organized and easy to read (to me at least).

Concluding this guide, I would like to reiterate, how useful I think Zend_Form is as it solves one of the most tedious process for any PHP project; form creation and validation. I hope it continues to evolve and mature, and I hope others find it as handy as I have. Although, if anyone has an easier solution, please let me know.