Zend Framework URLs without mod_rewrite

3rd June 2008

Some of our Zend Framework applications have to run on IIS without ISAPI_Rewrite installed. In these cases we need urls of the form http://www.example.com/index.php?module=mod&controller=con&action=act. I couldn't get this to work out of the box with Zend Framework 1.5, so wrote my own router called App_Controller_Router_Route_RequestVars.

This code obviously only supports what I needed and I've only tested it on IIS for Windows 2003 Server, so you may need to tweak to make it do what you want! Feel free to share any fixes :)

This is the code:

<?php

/** Zend_Controller_Router_Exception */
require_once 'Zend/Controller/Router/Exception.php';

/** Zend_Controller_Router_Route_Interface */
require_once 'Zend/Controller/Router/Route/Interface.php';

/**
 * Route
 *
 * @package    App_Controller
 * @subpackage Router
 * @copyright  Copyright (c) 2008 Rob Allen (rob@akrabat.com)
 */
class App_Controller_Router_Route_RequestVars implements Zend_Controller_Router_Route_Interface
{
    protected $_current = array();

    /**
     * Instantiates route based on passed Zend_Config structure
     */
    public static function getInstance(Zend_Config $config)
    {
        return new self();
    }

    /**
     * Matches a user submitted path with a previously defined route.
     * Assigns and returns an array of defaults on a successful match.
     *
     * @param string Path used to match against this routing map
     * @return array|false An array of assigned values or a false on a mismatch
     */
    public function match($path)
    {
        $frontController Zend_Controller_Front::getInstance();
        $request $frontController->getRequest();
        /* @var $request Zend_Controller_Request_Http */
        
        $baseUrl $request->getBaseUrl();
        if (strpos($baseUrl'index.php') !== false) {
            $url str_replace('index.php'"$baseUrl);
            $request->setBaseUrl($url);
        }
        
        $params $request->getParams();
        
        if (array_key_exists('module'$params)
                || array_key_exists('controller'$params)
                || array_key_exists('action'$params)) {
            
            $module $request->getParam('module'$frontController->getDefaultModule());
            $controller $request->getParam('controller'$frontController->getDefaultControllerName());
            $action $request->getParam('action'$frontController->getDefaultAction());

            $result = array('module' => $module, 
                'controller' => $controller, 
                'action' => $action, 
                );
            $this->_current $result;
            return $result;
        }
        return false;
    }

    /**
     * Assembles a URL path defined by this route
     *
     * @param array An array of variable and value pairs used as parameters
     * @return string Route path with user submitted parameters
     */
    public function assemble($data = array(), $reset=false)
    {
        $frontController Zend_Controller_Front::getInstance();
        
        if(!array_key_exists('module'$data) && !$reset 
            && array_key_exists('module'$this->_current)
            && $this->_current['module'] != $frontController->getDefaultModule()) {
            $data array_merge(array('module'=>$this->_current['module']), $data);
        }
        if(!array_key_exists('controller'$data) && !$reset 
            && array_key_exists('controller'$this->_current) 
            && $this->_current['controller'] != $frontController->getDefaultControllerName()) {
            $data array_merge(array('controller'=>$this->_current['controller']), $data);
        }
        if(!array_key_exists('action'$data) && !$reset 
            && array_key_exists('action'$this->_current)
            && $this->_current['action'] != $frontController->getDefaultAction()) {
            $data array_merge(array('action'=>$this->_current['action']), $data);
        }
        
        $url ";
        if(!empty($data)) {
            $urlParts = array();
            foreach($data as $key=>$value) {
                $urlParts[] = $key '=' $value;
            }
            $url '?' implode('&'$urlParts);
        }

        return $url;
    }
}

This route is then added to the Front Controller's router in my bootstrap like this:


$frontController Zend_Controller_Front::getInstance();
$router $frontController->getRouter();
$router->addRoute('requestVars', new App_Controller_Router_Route_RequestVars());

Hopefully this is a useful starting point for others who can't use mod_rewrite with Zend Framework.

Top Tip: XHTML with Zend Form Elements

29th May 2008

When you render a Zend_Form, the elements will render to HTML compliance rather than XHTML compliance, even if you have < ?php echo $this->doctype('XHTML1_STRICT');?> at the top of your layout script. Practically, this means that all the input elements do not end in "/>".

To resolve this, you need to call the doctype() view helper prior to rendering your form.

Within my projects, I do this within a front controller plug-in called ViewSetup that looks a little like this:


class App_Controller_Plugin_ViewSetup extends Zend_Controller_Plugin_Abstract
{
    public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
    {
        // setup the layout
        Zend_Layout::startMvc(); 
        
        $viewRenderer Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
        $viewRenderer->init();
        
        $view $viewRenderer->view;
        $view->doctype('XHTML1_TRANSITIONAL');

        $view->headMeta()->appendHttpEquiv('Content-Type''text/html;charset=utf-8′);
    }
}

Obviously, this class lives in the file library/App/Controller/Plugin/ViewSetup.php.

As you can see, I also set up the meta tag for the content-type to UTF-8 ready for rendering later.

Simple Zend_Form File Upload Example Revisited

16th May 2008

I've been thinking about the Simple Zend_Form File Upload Example that I discussed last month.

To recap, if you haven't read the comments, if the form fails to validate for some reason then you get a nasty error:

Warning: htmlspecialchars() expects parameter 1 to be string, object given in /Users/rob/Sites/akrabat/Zend_Form_FileUpload_Example/lib/Zend/View/Abstract.php on line 786

Essentially, what is happening is that the App_Form_Element_File class that we wrote assigns the $_FILES array to the $value parameter for the form element. On redisplay of the form, the formFile view helper then calls the escape() view helper passing in the $value when rendering the <input> element. The escape() view helper calls htmlspecialchars() which throws the warning about $value not being a string.

*whew!*

What we need is something that's an array when the data is valid, but can also look like a string to htmlspecialchars(). This got me thinking about the SPL and creating an object for the data from the $_FILES array.

Let's call this object App_Form_Element_FileValue and store it in lib/App/Form/Element/FileValue.php:

<?php

class App_Form_Element_FileValue extends ArrayObject
{
    public function __toString()
    {
        $result ";
        if(isset($this->name)) {
            $result $this->name;
        }
        return $result;
    }
}

The ArrayObject class is part of the SPL and handily provides a set of functions that enables the object to work with most functions that we like to use with an array including the ability to access the data using array notation. We implement the PHP5 magic function __toString() so that htmlspecialchars() will get a string from the object when it asks for one which nicely knocks that problem on the head.

To integrate it into the code, we need to modify App_Form_Element_File::isValid() from:


    public function isValid($value$context null)
    {
        // for a file upload, the value is not in the POST array, it's in $_FILES
        $key $this->getName();
        if(null === $value) {
            if(isset($_FILES[$key])) {
                $value $_FILES[$key];
            }
        }
        // continues…

to


    public function isValid($value$context null)
    {
        // for a file upload, the value is not in the POST array, it's in $_FILES
        $key $this->getName();
        if(null === $value) {
            if(isset($_FILES[$key])) {
                $value = new App_Form_Element_FileValue($_FILES[$key]);
            }
        }
        // continues…

We also need to modify the validator App_Validate_ValidFile::isValid() function as it's rather too rigourous in its checking. We currently check that $value is an array using is_array():


    public function isValid($value)
    {
        // default value and error is "no file uploaded"
        $valueString ";
        $error UPLOAD_ERR_NO_FILE;
        
        if(is_array($value) && array_key_exists('error'$value)) {
            // set the error to the correct value
            $error $value['error'];
            
            // set the %value% placeholder to the uplaoded filename
            $valueString $value['name'];
        }
        // continues…

As $value is now an object of type App_Form_Element_FileValue, we need to change the test in the if statement to:


    public function isValid($value)
    {
        // default value and error is "no file uploaded"
        $valueString ";
        $error UPLOAD_ERR_NO_FILE;
        
        if((is_array($value) || $value instanceof ArrayObject) 
            && array_key_exists('error'$value)) {
            // set the error to the correct value
            $error $value['error'];
            
            // set the %value% placeholder to the uplaoded filename
            $valueString $value['name'];
        }
        // continues…

Note that we test for an instance of ArrayObject as that is where the functionality of array behaviour is implemented and is more generic in case we need to reuse this code with another object that behaves like an array.

Those are the only changes needed to elegantly remove the error message.

Here's a zip file of this project with the above changes: Zend_Form_FileUpload_Example_Revisited.zip (It includes Zend Framework 1.5.2 which is why it's 3.9MB big).

Test it out and see if it works for you as well as it works for me !

Simple Zend_Form File Upload Example

7th April 2008

Zend Framework 1.5's Zend_Form component is missing support for the file input element as it is waiting on a file upload component to build upon. We're busy people, so we'll fake it…

This is a super simple example showing how to do file uploads with Zend_Form in Zend Framework 1.5.

I'm building on the Simple Zend_Form Example, so make sure you have read that and that it works before you start this one.

This is what the form looks like:

SimpleFileUploadScreenshot.jpg

(Unstyled, as usual!)

This is what we need to do:

The form

The form is an extension of Zend_Form and is stored in app/forms/UploadForm.php and so the class name is forms_UploadForm:

<?php

class forms_UploadForm extends Zend_Form 
{ 
    public function __construct($options null) 
    { 
        $this->addElementPrefixPath('App''App/');
        parent::__construct($options);
        $this->setName('upload');
        $this->setAttrib('enctype''multipart/form-data');
        
        $description = new Zend_Form_Element_Text('description');
        $description->setLabel('Description')
                  ->setRequired(true)
                  ->addValidator('NotEmpty');

        $file = new App_Form_Element_File('file');
        $file->setLabel('File')
                 ->setRequired(true)
                 ->addValidator('NotEmpty');
             

        $submit = new Zend_Form_Element_Submit('submit');
        $submit->setLabel('Upload');
        
        $this->addElements(array($description$file$submit));
            

    } 
} 

We are going to create our own validator class within our own library namespace (lib/App) and so we need to tell the form about this using addElementPath(). We then set the name and enctype attribute of the form to allow for files to be uploaded.

The form has two fields: a text field called 'description' and the file upload field called 'file', along with a submit button. As Zend Framework doesn't have it's own file element, we will create our own called App_Form_Element_File.

The file form element

The file element, App_Form_Element_File, is stored in lib/App/Form/Element/File.php and looks like this:

<?php
require_once 'Zend/Form/Element/Xhtml.php';

class App_Form_Element_File extends Zend_Form_Element_Xhtml
{
    /**
     * Flag indicating whether or not to insert ValidFile validator when element is required
     * @var bool
     */
    protected $_autoInsertValidFileValidator true;

    /**
     * Default view helper to use
     * @var string
     */
    public $helper 'formFile';
    
    /**
     * Set flag indicating whether a ValidFile validator should be inserted when element is required
     * 
     * @param  bool $flag 
     * @return Zend_Form_Element
     */
    public function setAutoInsertValidFileValidator($flag)
    {
        $this->_autoInsertValidFileValidator = (bool) $flag;
        return $this;
    }

    /**
     * Get flag indicating whether a ValidFile validator should be inserted when element is required
     * 
     * @return bool
     */
    public function autoInsertValidFileValidator()
    {
        return $this->_autoInsertValidFileValidator;
    }
    
    
    public function isValid($value$context null)
    {
        // for a file upload, the value is not in the POST array, it's in $_FILES
        $key $this->getName();
        if(null === $value) {
            if(isset($_FILES[$key])) {
                $value $_FILES[$key];
            }
        }
        
        // auto insert ValidFile validator
        if ($this->isRequired()
            && $this->autoInsertValidFileValidator()
            && !$this->getValidator('ValidFile'))
        {
            $validators $this->getValidators();
            $validFile   = array('validator' => 'ValidFile''breakChainOnFailure' => true);
            array_unshift($validators$validFile);
            $this->setValidators($validators);
            
            // do not use the automatic NotEmpty Validator as ValidFile replaces it 
            $this->setAutoInsertNotEmptyValidator(false);
        }

        return parent::isValid($value$context);
    }

}

Zend Framework already provides a view helper for displaying a file element, formFile, so we set the $helper member variable to 'formFile' so that the correct element is rendered when the form is displayed. We then need to ensure that validation is handled correctly. For all other form elements the value of the field is returned in the POST array. For a file, this is not true as the data is with the $_FILES global array. We could handle this in the controller, but by putting it in the element class, we never have to think about it again. The isValid() member function is used to set the value for an element and also run the validator chain to determine if the value is valid or not.

We override isValid() for the file element to provide two functionalities:

* Set the value to the contents of correct sub-array of the $_FILES array .
* Automatically turn on a custom validator called ValidFile which will check if the upload succeeded.

The file element also has some helper functions (setAutoInsertValidFileValidator and getAutoInsertValidFileValidator) to control the auto-insertion of the ValidFile validator. Note that if we do automatically insert the ValidFile validator, then we turn off Zend_Form_Element's automatic NotEmpty validator as it is redundant.

Once we have set the $value variable correctly and inserted our validator, we call up to the parent's isValid() function which will run the validation chain for us.

The ValidFile validator

The ValidFile validator's full class name is App_Validate_ValidFile and so it's filename is lib/App/Validate/ValidFile.php. This class handles validating the 'error' field within the $value array (which as you'll recall came from $_FILES). This is the code:

<?php

class App_Validate_ValidFile extends Zend_Validate_Abstract
{

    const INI_SIZE 'iniSize';     // Value: 1; The uploaded file exceeds the upload_max_filesize directive in php.ini
    const FORM_SIZE 'formSize';   // Value: 2; The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form. 
    const PARTIAL 'partial';      // Value: 3; The uploaded file was only partially uploaded. 
    const NO_FILE 'noFile';       // Value: 4; No file was uploaded. 
    const NO_TMP_DIR 'noTmpDir';  // Value: 6; Missing a temporary folder.
    const CANT_WRITE 'cantWrite'// Value: 7; Failed to write file to disk.
    const EXTENSION 'extension';  // Value: 8; File upload stopped by extension. Introduced in PHP 5.2.0. 
    const ERROR 'error';          // General error for future proofing against new PHP versions

    /**
     * @var array
     */
    protected $_messageTemplates = array(
        self::INI_SIZE => "The uploaded file exceeds the upload_max_filesize directive in php.ini",
        self::FORM_SIZE => "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form",
        self::PARTIAL => "The uploaded file was only partially uploaded",
        self::NO_FILE => "No file was uploaded",
        self::NO_TMP_DIR => "Missing a temporary folder",
        self::CANT_WRITE => "Failed to write file to disk",
        self::EXTENSION => "File upload stopped by extension",
        self::ERROR => "Unknown upload error"
    );

    /**
     * Defined by Zend_Validate_Interface
     *
     * Returns true if and only if $value[error] is equal to UPLOAD_ERR_OK.
     * 
     * @note: This validator expects $value to be the array from $_FILES
     *
     * @param array $value
     * @return boolean
     */
    public function isValid($value)
    {
        // default value and error is "no file uploaded"
        $valueString ";

        $error UPLOAD_ERR_NO_FILE;
        
        if(is_array($value) && array_key_exists('error'$value)) {
            // set the error to the correct value
            $error $value['error'];
            
            // set the %value% placeholder to the uplaoded filename
            $valueString $value['name'];
        }
        
        $this->_setValue($valueString);

        $result false;
        switch($error) {
            case UPLOAD_ERR_OK:
                $result true;
                break;
                
            case UPLOAD_ERR_INI_SIZE:
                $this->_error(self::INI_SIZE);
                break;
                
            case UPLOAD_ERR_FORM_SIZE:
                $this->_error(self::FORM_SIZE);
                break;
                
            case UPLOAD_ERR_PARTIAL:
                $this->_error(self::PARTIAL);
                break;
                
            case UPLOAD_ERR_NO_FILE:
                $this->_error(self::NO_FILE);
                break;
                
            case UPLOAD_ERR_NO_TMP_DIR:
                $this->_error(self::NO_TMP_DIR);
                break;
                
            case UPLOAD_ERR_CANT_WRITE:
                $this->_error(self::CANT_WRITE);
                break;
                
            case 8// UPLOAD_ERR_EXTENSION isn't defined in PHP 5.1.4, so use the value
                $this->_error(self::EXTENSION);
                break;
                
            default:
                $this->_error(self::ERROR);
                break;
        }

        return $result;
    }

}

Although this class is quite long, not much happens! All the work happens in isValid() where we set $error to the 'error' value originally from the $_FILES array and then set our error message based on which error PHP has reported to us. If PHP reported no error (UPLOAD_ERR_OK), then we return true, otherwise we return false after having set the error value.

The controller

Lastly, the controller is essentially identical to what we did in the Simple Zend_Form Example:

<?php

class IndexController extends Zend_Controller_Action
{
    function indexAction()
    {
        $this->view->pageTitle "Zend_Form File Upload Example";
        $this->view->bodyCopy "<p>Please fill out this form.</p>";

        $form = new forms_UploadForm();

        if ($this->_request->isPost()) {
            $formData $this->_request->getPost();
            if ($form->isValid($formData)) {
                
                // success - do something with the uploaded file
                $uploadedData $form->getValues();
                Zend_Debug::dump($uploadedData'$uploadedData');
                exit;
                
            } else {
                $form->populate($formData);
            }
        }

        $this->view->form $form;
    }
}

If it all validates correctly, then you need to process the uploaded data. For this example we just display it using Zend_Debug::dump(), so you can see what it looks like:

SimpleFileUploadScreenshot2.jpg

Conclusion

As you can see, although Zend Framework 1.5 doesn't have file upload element built in, adding one is not especially hard. In fact, we have more code used to validate that and provide an error message on failure than we do for the upload itself!

As usual, here's a zip file of this project: Zend_Form_FileUpload_Example.zip (It includes Zend Framework 1.5.1 which is why it's 3.9MB big).

Test it out and maybe use it as the basis of your file uploading needs with Zend_Form.

Update: There's a follow up to this post here: Simple Zend_Form File Upload Example Revisited.

Updated Tutorial for Zend Framework 1.5

17th March 2008

Zend Framework 1.5 has now been released to mark the occasion I have significantly updated my Zend Framework Tutorial! The tutorial was first released on 16th August 2006 and was written against version 0.1 of Zend Framework and had one major revision to bring in support for the ViewRenderer component.

Tutorial-Screenshot

The new tutorial produces exactly the same application as before, but now uses the new 1.5 goodies of Zend_Form and Zend_Layout, so you can see how these key components fit into a Zend Framework MVC application.

As always, when you find bugs and typos, please let me know and I'll fix them!

Simple Zend_Form Example

21st February 2008

Following on from the Simple Zend_Layout Example, Zend_Form is now in the trunk, so here's a super simple, complete example that shows it in action:

Zend_Form screenshot1.png

(clearly the form is unstyled :)

You can construct a Zend_Form directly from a config file or can build it in code. This example builds it in code.

Setting up

This example uses the same basic skeleton as the Zend_Layout code with the addition of a forms directory:

Zend_Form Directory Layout.png

As you can see, I've added a forms directory within the application subdirectory with our form file, ContactForm.php, in it.

The contact form

To keep the form contained, I've put it in its own class which extends from Zend_Form. The file is application/forms/ContactForm.php and so the class is forms_ContactForm so that Zend_Loader can easily load it:


class forms_ContactForm extends Zend_Form 
{ 
    public function __construct($options null) 
    { 
        parent::__construct($options);
        $this->setName('contact_us');
        
        $title = new Zend_Form_Element_Select('title');
        $title->setLabel('Title')
              ->setMultiOptions(array('mr'=>'Mr''mrs'=>'Mrs'))
              ->setRequired(true)->addValidator('NotEmpty'true);
        
        $firstName = new Zend_Form_Element_Text('firstName');
        $firstName->setLabel('First name')
                  ->setRequired(true)
                  ->addValidator('NotEmpty');

        $lastName = new Zend_Form_Element_Text('lastName');
        $lastName->setLabel('Last name')
                 ->setRequired(true)
                 ->addValidator('NotEmpty');
             
        $email = new Zend_Form_Element_Text('email');
        $email->setLabel('Email address')
              ->addFilter('StringToLower')
              ->setRequired(true)
              ->addValidator('NotEmpty'true)
              ->addValidator('EmailAddress'); 
              
        
        $submit = new Zend_Form_Element_Submit('submit');
        $submit->setLabel('Contact us');
        
        $this->addElements(array($title$firstName, 
            $lastName$email$submit));
        
    } 
}

We set the name of the form first and then create five elements; one select box for the title, three text boxes for first name, surname and email address and then finally a submit button. Note that as we have overridden the constructer, we also call up to the parent's constructor so that all the Zend_Form initialisation happens.

Creating an element is quite simple. Each element is of the type Zend_Form_Element_Xxx where Xxx is the type, such as Text, Submit, Select, etc. We then configure the element how we want it, such as setting a label and adding filters and validators. Finally we add all the elements to the form using the addElements() function.

Note that if you have multiple validators, you can pass in true as the second parameter ($breakChainOnFailure) to stop processing the remaining validators at that point. This can be because the further validators do not make sense or because you only ever want the user to see one message per field to avoid overwhelming them.

Displaying the form

Having defined out form, we need to get it to the screen. In the controller we need the following:


class IndexController extends Zend_Controller_Action
{
    function indexAction()
    {
        $this->view->pageTitle "Zend_Form Example";
        $this->view->bodyCopy "<p >Please fill out this form.</p>";

        $form = new forms_ContactForm();
        $this->view->form $form;
    }
}

This code assigns some variables to the view, then instantiates the form and assigns that to the view also. The view script is similarly simple:

view/scripts/index/index.phtml:

<?php echo $this->pageTitle ;?>
<?php echo $this->bodyCopy ;?>

<?php echo $this->form ;?>

To render the form, we simply echo it. By default, the form is marked up as a definition list and its HTML looks like this:


<form name="contact_us" id="contact_us" enctype="application/x-www-form-urlencoded" action="" method="post"><dl class="zend_form">
<dt><label for="title" class="required">Title</label></dt>
<dd>
<select name="title" id="title">
    <option value="mr" label="Mr">Mr</option>
    <option value="mrs" label="Mrs">Mrs</option>
</select></dd>
<dt><label for="firstName" class="required">First name</label></dt>
<dd>
<input type="text" name="firstName" id="firstName" value=""></dd>
<dt><label for="lastName" class="required">Last name</label></dt>
<dd>
<input type="text" name="lastName" id="lastName" value=""></dd>
<dt><label for="email" class="optional">Email address</label></dt>
<dd>
<input type="text" name="email" id="email" value=""></dd>
<dt></dt><dd>
<input type="submit" name="submit" id="submit" value="Contact us"></dd></dl></form>

Notice how the setRequired() has resulted in the "required" CSS class name being applied to those elements.

Decorators

If you do not want to use a definition list, then this can easily be changed using decorators. For instance, adding the following to the bottom of the form's constructor after the addElements() call:


        $this->clearDecorators();
        $this->addDecorator('FormElements')
         ->addDecorator('HtmlTag', array('tag' => '<ul>'))
         ->addDecorator('Form');
        
        $this->setElementDecorators(array(
            array('ViewHelper'),
            array('Errors'),
            array('Description'),
            array('Label', array('separator'=>' ')),
            array('HtmlTag', array('tag' => 'li''class'=>'element-group')),
        ));

        // buttons do not need labels
        $submit->setDecorators(array(
            array('ViewHelper'),
            array('Description'),
            array('HtmlTag', array('tag' => 'li''class'=>'submit-group')),
        ));

will result in the form being marked up as an unsigned list with each label and element pair inside each <li>. The marked up form looks like this:


<form name="contact_us" id="contact_us" enctype="application/x-www-form-urlencoded" action="" method="post"><ul>
<li class="element-group"><label for="title" tag="" class="required">Title</label> 
<select name="title" id="title">
    <option value="mr" label="Mr" selected="selected">Mr</option>
    <option value="mrs" label="Mrs">Mrs</option>
</select></li>
<li class="element-group"><label for="firstName" tag="" class="required">First name</label> 
<input type="text" name="firstName" id="firstName" value=""></li>
<li class="element-group"><label for="lastName" tag="" class="required">Last name</label> 
<input type="text" name="lastName" id="lastName" value=""></li>
<li class="element-group"><label for="email" tag="" class="required">Email address</label> 
<input type="text" name="email" id="email" value=""></li>
<li class="submti-group"><input type="submit" name="submit" id="submit" value="Contact us"></li></ul></form>

As you can see, the decorator system makes it easy to render our form in the manner we wish. As a note of warning, pay attention to the order of the decorators as it matters!

Processing

The form is now displayed, so when the user presses the button we need to process it. In this example, this is done in the indexAction() controller function, so that the complete function looks like this with the addition in bold:


class IndexController extends Zend_Controller_Action
{
    function indexAction()
    {
        $this->view->pageTitle "Zend_Form Example";
        $this->view->bodyCopy "<p>Please fill out this form.</p>";

        $form = new forms_ContactForm();

        if ($this->_request->isPost()) {
            $formData $this->_request->getPost();
            if ($form->isValid($formData)) {
                echo 'success';
                exit;
            } else {
                $form->populate($formData);
            }
        }

        $this->view->form $form;
    }
}

Firstly we check the the request is a POST using the request object's isPost() function and then we use validate the submitted data using the form's isValid() function. If this returns true, then we have valid data and can process. If the d