Lifting the Skirt on Zend Framework 1.5 – Zend_Form

January 29, 2008

News, Zend Framework

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.

About Cal Evans

Many moons ago, at the tender age of 14, Cal touched his first computer. (We're using the term "computer" loosely here, it was a TRS-80 Model 1) Since then his life has never been the same. He graduated from TRS-80s to Commodores and eventually to IBM PC's. For the past 10 years Cal has worked with PHP and MySQL on Linux OSX, and when necessary, Windows. He has built on a variety of projects ranging in size from simple web pages to multi-million dollar web applications. When not banging his head on his monitor, attempting a blood sacrifice to get a particular piece of code working, he enjoys building and managing development teams using his widely imitated but never patented management style of "management by wandering around". Cal is currently based in Nashville, TN and is gainfully unemployed as the Chief Marketing Officer of Blue Parabola, LLC. Cal is happily married to wife 1.28, the lovely and talented Kathy. Together they have 2 kids who were both bright enough not to pursue a career in IT. Cal blogs at http://blog.calevans.com and is the founder and host of Day Camp 4 Developers

View all posts by Cal Evans

28 Responses to “Lifting the Skirt on Zend Framework 1.5 – Zend_Form”

  1. justinwoods Says:

    This is absolutely awesome! I have been wishing for a long time for a clean way to create and validate forms programatically.

    I think I’m going to go rejoice in the streets now, singing and dancing.

  2. joenilson Says:

    I only can say one thing to the wonderful framework dev team, how said Linda Blair: "Mommy tell him to stop!", you kill me boys, now i will forget the HTML at last.

    God bless you!!

  3. dinoboff Says:

    That’s a great helper and thanks for this example, especially the part using an INI file.

    Is there an easy way to add a fieldset?

  4. weierophinney Says:

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

  5. sicouk Says:

    Can someone explaint he benefit to storing the form as an ini file or zend_config object? I can’t see the advantage if I’m honest. Why not just store the array in an include or the like?

  6. weierophinney Says:

    @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:

    $options = include 'form_config.php'; // form_config.php should return an array
    $config  = new Zend_Config($options);
    

    You could also simply pass the array directly to Zend_Form’s constructor; it
    can utilize either an array or a Zend_Config object.

  7. _____anonymous_____ Says:

    Thanks for the great example! It’s made everytyhing so clear, but where do I save the files to get it working? Is it a controller file etc? Thanks!

  8. tariquesani Says:

    I very much like the elegance of the form class in terms of it syntax but making the controller actions dependent on a form is something I don’t like.

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

  9. bossakungen Says:

    Indeed great, been waiting for a feature like this for a long time. Making forms with PHP and HTML can get messy and this will definitely help clean things up.

  10. weierophinney Says:

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

  11. weierophinney Says:

    @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:

    • Validation
    • Filtering
    • Rendering
    • Ordering/Iteration

    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. :-)

  12. joeyadms Says:

    The encapsulation provided by Zend_Form is just another reason why I am in love with the framework. Sure, I could spend endless time in escaping and writing validation methods and etc etc etc for a simple form, or learn, god forbid, smarty template programming, eek, but this gives developers a direct API to forms, and I say bring it on, the more html we can do programatically is better. It’s all about not reinventing the wheel.

    Great work guys.

  13. _____anonymous_____ Says:

    Don’t want to boast, but I’ve implemented similar idea about 3 years ago. The major difference is that form configuration was described in custom XML format. Here’s example:

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

  14. chelala Says:

    Hi

    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?

  15. deusx Says:

    Nice job with the Zend_Form module. All my respect to the author and I hope he will continue his work in the future.
    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.

  16. weierophinney Says:

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

  17. weierophinney Says:

    @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:

    
    form ?>
    

    Basically, the MVC usage does not differ much from the standard procedural
    usage.

  18. baonhan Says:

    it’s convenient to have that form. it looks similar to the drupal one. Why don’t we make some set methods for form’s attributes instead of that messy array?

  19. baonhan Says:

    sorry sorry… I only looked at the one with array, didn’t see the above one. That’s what I mean what it should have. Zend is great!

  20. kwaadschiks Says:

    Is it possible to autoload my own /path/to/a/set/of/validators? Without having to extend the elements, or passing the prefixpath to every new form object.

    Ty.

  21. _____anonymous_____ Says:

    Looks like a very usable system! And its always nice to use your own stuff, you know how everything works, etc.

    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!

  22. dsims Says:

    I am currently using this in a project I am working on
    This located in library/Ca/Form/Default.php
    <code>
    <?php

    class Ca_Form_Default extends Zend_Form
    {
    public $login;
    public function deafultforms($login)
    {
    $fname = $this->createElement(
    ‘text’, ‘fname’, array(
    ‘label’ => ‘First name:’,
    ‘required’ => true,
    ‘filters’ => array(‘StringTrim’),
    ‘validators’ => array(
    ‘Alpha’
    )));
    $lname = $this->createElement(
    ‘text’, ‘lname’, array(
    ‘label’ => ‘Last name:’,
    ‘required’ => true,
    ‘filters’ => array(‘StringTrim’),
    ‘validators’ => array(
    ‘Alpha’
    )));
    $city = $this->createElement(
    ‘text’, ‘city’, array(
    ‘label’ => ‘City:’,
    ‘required’ => true,
    ‘filters’ => array(‘StringTrim’),
    ‘validators’ => array(
    ‘Alpha’
    )));
    $state = $this->createElement(
    ‘text’, ‘state’, array(
    ‘label’ => ‘State:’,
    ‘required’ => true,
    ‘filters’ => array(‘StringTrim’),
    ‘validators’ => array(
    ‘Alpha’
    )));
    $zip = $this->createElement(
    ‘text’, ‘zip_code’, array(
    ‘label’ => ‘zip code:’,
    ‘required’ => true,
    ‘filters’ => array(‘StringTrim’),
    ‘validators’ => array(
    array(‘regex’, false, ‘/^[0-9\-]/i’)
    )));
    // add username element
    $username = $this->createElement(
    ‘text’, ‘username’, array(
    ‘label’ => ‘Username:’,
    ‘required’ => true,
    ‘filters’ => array(‘StringTrim’),
    ‘validators’ => array(
    array(‘regex’, false, ‘/^[a-zA-Z0-9_]/i’)
    )));
    // add an email element
    $email = $this->createElement(
    ‘text’, ‘email’, array(
    ‘label’ => ‘Your email address:’,
    ‘required’ => true,
    ‘filters’ => array(‘StringTrim’),
    ‘validators’ => array(
    ‘EmailAddress’
    )));
    // add element password
    $password = $this->createElement(
    ‘password’, ‘password’, array(
    ‘label’ => ‘Password:’,
    ‘required’ => true,
    ‘filters’ => array(‘StringTrim’),
    ‘validators’ => array(
    array(‘regex’, false, ‘/^[a-zA-Z0-9]/i’)
    )));
    // add element captcha
    $captcha = new Zend_Form_Element_Captcha(
    ‘captcha’, array(
    ‘captcha’ => array(
    ‘captcha’ => ‘Image’,
    ‘wordLen’ => 6,
    ‘timeout’ => 300,
    // BASEURL defined in index.php
    ‘imgUrl’ => BASEURL . ‘images/captcha’,
    ‘width’ => 200,
    ‘height’ => 60,
    ‘font’ => ‘/var/www/html/zends/zendspace/img/captcha/Activa.ttf’
    )));
    // AdminInterface Form Elements
    // to be written
    // UserInterface Form Elements
    // to be written
    $dlogin = array($username, $password, $captcha);
    $dreg = array($fname, $lname, $city, $state, $zip, $username, $password, $email, $captcha);
    if($login == ‘login’){
    return $this->addElements($dlogin);
    }elseif($login == ‘reg’){
    return $this->addElements($dreg);
    }
    }
    }
    </code>
    It’s very sparse for validators and filters, basically because I am still learning. I basically would like to use a class like this because of KISS. If I ever want to change anything I just go to this class and change what I need. My question is am I on the right path and if not any guidance would be appreciated.
    Thanks,
    Dave

  23. electricgraffitti Says:

    Very good post.

    I do have a question. I already have my elements created in an ini file with the validations in place.

    my confusion is how to be able to have those work from inside my action methods?

    ex.

    i have a
    public function infoRequestPostAction() {
    if ($this->getRequest->isPost()) {

    //validate elements

    }
    }

    something like that

    any guidance would be \m/>.<\m/

  24. _____anonymous_____ Says:

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

    Why couldn’t you just provide the link? I can’t find it!!!

    http://devzone.zend.com/article/3030-Lifting-the-Skirt-on-Zend-Framework-1.5—Zend_Form

  25. jay3000bc Says:

    I know this is the simplest way of making forms.
    But actually forms are not that simple – it will have things like, Field Info, Icons etc

    For example a message just above the Text Area in your Blog [Total words should be more than 500 characters and less than 2000 characters]

    or you may need to graphically show how secure is the password or length of password etc.

    So, under such circumstances, which one will you prefer.

  26. rajaboys Says:

    I have created form in config/form.ini

    ————–form.ini—————————-
    [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"

    ——————-End Form————————————

    Then i place the below code in User Controller

    —————-User Controller——————————–
    $formConfig = new Zend_Config_Ini(APPLICATION_PATH.’/configs/form.ini’);
    $newUserForm = new Zend_Form($formConfig);
    $this->view->newUserForm = $newUserForm;

    ——–End USer Controller————————————-
    then place the below code in view/login.phtml

    ———–Login.phtml—————————————–

    echo $this->newUserForm;

    ——————-End Login.phtml—————————-

    now i try to run

    http://localhost/user/login

    nothing display except my layout. kindly guide me where went wrong?

  27. weierophinney Says:

    It’s how you’re grabbing your configuration. Use the following:

    // Make sure you grab the "development" section, as that’s where you defined the configuration
    $formConfig = new Zend_Config_Ini(APPLICATION_PATH . ‘/configs/form.ini’, ‘development’);

    // Make sure you grab the appropriate part of the form configuration — which you namespaced at "user.login"
    $newUserForm = new Zend_Form($formConfig->user->login);

  28. rajaboys Says:

    thank u..now its working fine…..