Spiga

Using Zend_Form with Zend_Config

March 17, 08 by Andrew Vayanis

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:

login.elements.username.type = "text"

And turns it into:

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

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

in PHP becomes

    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…

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.

    ; 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.

    [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]. Tthis 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.

Similar Posts

Add your comment

7 responses for this post

  1. Gravatar
    Vincent Says:

    Thanks for the article! it was very very helpful.

    I love using ini config files, it makes maintaining code a million times easier than messing with php coding.

    but i’ve hit a brick wall. i’ve tried setting custom validation error messages with no luck… do you have any ideas?

    ; orderFullName element
    cartForm.elements.orderFullName.type = text
    cartForm.elements.orderFullName.options.label = Full Name
    cartForm.elements.orderFullName.options.size = 25
    cartForm.elements.orderFullName.options.maxlength = 25
    cartForm.elements.orderFullName.options.validators.strlen.validator = StringLength
    cartForm.elements.orderFullName.options.validators.strlen.options.min = 2
    cartForm.elements.orderFullName.options.validators.strlen.options.max = 25
    cartForm.elements.orderFullName.options.required = true
    cartForm.elements.orderFullName.options.required.setMessages = Required field

  2. Gravatar
    Andrew Vayanis Says:

    I was actually going to throw in another post regarding this, but I will show you now what I did to set custom validation messages because the defaults are definitely horrible.

    elements.username.type = “text”
    elements.username.options.label = “Username:”
    elements.username.options.required = true
    elements.username.options.validators.notempty.validator = “NotEmpty”
    elements.username.options.validators.notempty.options.messages.isEmpty = “A valid username is required.”
    elements.username.options.validators.notempty.breakChainOnFailure = true
    elements.username.options.validators.alnum.validator = “alnum”
    elements.username.options.validators.alnum.breakChainOnFailure = true
    elements.username.options.validators.alnum.options.messages.notAlnum = “Invalid username. Please enter a valid username.”
    elements.username.options.validators.strlen.validator = “StringLength”
    elements.username.options.validators.strlen.options.min = “5″
    elements.username.options.validators.strlen.options.max = “30″
    elements.username.options.validators.strlen.options.messages.stringLengthTooShort = “Invalid username. Please enter a valid username.”
    elements.username.options.validators.strlen.options.messages.stringLengthTooLong = “Invalid username. Please enter a valid username.”

    I am still learning the Zend Framework myself, but I hope this helps. Also, feel free to ask any other questions, and I will answer what I can.

  3. Gravatar
    VINCENT Says:

    Thanks Andrew for the reply, I really appreciate the reply, you got me out of a big hole (I was about to abandon config files for my forms and use nasty php).

    I’m also still learning the Zend Framework (24 hours into it) and I’ve decided to completely rebuild my apps using it :).

    I think its a very good framework to use, it is a bit of a learning curve to get into the MVC pattern, but after that, its pretty easy. Most of it is just getting around the idea of a MVC file structure and a bootstrapper.

    I must say however, the Zend Framework does provide very good documentation on the PHP side, but it does lack documentation on formatting config ini files and config xml files, which is a shame because all web 2.0 websites driven by AJAX is built on the idea of text config files. I hope that they fix that up soon.

    Other than that, thank god for open source discussion.

    btw. how did you figure out to use messages instead of setmessages (from setMessages();)?

  4. Gravatar
    Andrew Vayanis Says:

    Don’t want this to come off as snobby, so I preface before saying it, but as stated in the post, when ZF looks up class methods using the Zend_Config, method calls are prepended with ’set’. Therefore, internally, ZF is actually calling setMessages().

    As you said the documentation is pretty good on the PHP side, and a bit lacking on the config side. However, there is also a bit of a disconnect which I will hope to bridge (with my own posts) as I learn the framework myself. The tidbit about prepending ’set’ to method calls is just one of many examples I am sure you will find as you go through things.

  5. Gravatar
    Matthew Weier O'Phinney Says:

    Something to note: you were reading the documentation prior to the final release of 1.5.0. I put a lot of work into the documentation in that final week, and the results are online now.

    In the documentation, there is a ‘Configuration’ section for each subcomponent of Zend_Form. In these, I have language such as the following:

    * If ’set’ + key refers to a Zend_Form_Element method, then the value provided will be passed to that method.
    * Otherwise, the value will be used to set an attribute.

    Hopefully this addresses your documentation concerns with Zend_Form. I realize there are a lot more places where it needs improvement — e.g., more examples on decorators — but hopefully your primary concerns have been addressed.

  6. Gravatar
    Andrew Vayanis Says:

    I just went to look at the new documentation, and this does address my concerns. However, it might be worth pointing out that these types of disconnects, are prevalent elsewhere within the Zend Framework’s documentation. Unless, they have also been addressed with the release of 1.5?

    Anyhow, I still like how things are coming, keep up the good work!

  7. Gravatar
    Apit Says:

    Hi. I am new zfform. Your post helps me understand and appeciate customizability of zfform more. Thx.

Leave a Reply