May 10, 2008 10:21:42 AM

Tags: PHP Zend Framework

One feature that I believe is lacking from an out-of-the-box Zend_Form setup is form-level errors. Form-level errors are not tied to one particular element; instead, they apply to the form as a whole. Examples might include 'Invalid login credentials' (which applies to entire login form), or 'There was an error processing your form' (if a database query fails). These types of error messages are not catered for.

There are a number of ways to achieve form-level errors. The easiest is to stick the error message in the view as $formError and just render it above $form->render() if it is set. I like this solution because it doesn't require any subclassing, but I'd prefer to have a solution which renders form-level errors as part of $form->render().

<?php

// controller

$form = new Zend_Form(...);

$this->view->form = $form;

if ($this->getRequest()->isPost())
{
	if ($form->isValid($_POST))
	{
		try
		{
			// ...
		}
		catch (Exception $e)
		{
			$this->view->formError = '....';
		}
	}
}

// view

if ($this->formError !== NULL)
{
	echo $this->formError;
}

echo $this->form->render();

Nice and simple, but doesn't really encapsulate all form handling within Zend_Form. Furthermore, using this technique couples the controller and view more strongly, and I'd say that goes somewhat against the Zend Framework methodology.

So an alternative, and the mechanism I'm using now, is to subclass Zend_Form. Cosider App_Form below:

<?php

class App_Form extends Zend_Form
{
	protected $_formError = NULL;

	function __construct ($options = NULL)
	{
		$this->setDisableLoadDefaultDecorators(true);		
		$this->addPrefixPath('App_Form', 'App/Form/');
		parent::__construct($options);
		$this	->addDecorator('FormElements')
			->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form'))
			->addDecorator('FormError')
			->addDecorator('Form');
	}

	public function setFormError ($error)
	{
		if (is_array($error))
		{
			$this->_formError = $error;
		}
		else if (is_string($error))
		{
			$this->_formError = array($error);
		}
		else
		{
			throw new Zend_Exception('$error must be a string or an array');
		}
	}

	public function addFormError ($error)
	{
		if (! is_string($error))
		{
			throw new Zend_Exception('$error must be a string');
		}
		if (is_array($this->_formError))
		{
			$this->_formError[] = $error;
		}
		else
		{
			$this->_formError = array($error);
		}
	}

	public function getFormError ()
	{
		return $this->_formError;
	}

	public function isFormError ()
	{
		return $this->_formError !== NULL;
	}
}

The constructor above creates a standard form, with the addition of the FormError decorator, shown below. The other functions in App_Form write and read the form-level errors.

<?php

class App_Form_Decorator_FormError extends Zend_Form_Decorator_Abstract
{
	public function render ($content)
	{
		$form = $this->getElement();
		
		$errorContent = '';
		
		if ($form instanceof App_Form)
		{
			if ($form->isFormError())
			{
				$errorContent = '<ul><li>';
				$errorContent .= join('</li><li>', $form->getFormError);
				$errorContent .= '</li></ul>';
			}
		}
		
		return $errorContent.$content;
	}
}

My FormError decorator just shows a list of errors above the DL that contains the form elements. A nice thing to note is that the order in which decorators are added to the form is important: if you switch the addition of the Form and FormError decorators in App_Form's constructor, the list of errors will appear above the <form> tag.

Putting it all together:

<?php

// controller

$form = new App_Form(...);

$this->view->form = $form;

if ($this->getRequest()->isPost())
{
	if ($form->isValid($_POST))
	{
		try
		{
			// ...
		}
		catch (Exception $e)
		{
			$form->addFormError('...');
		}
	}
}

// view

echo $this->form->render();

Any other suggestions?

Recent Posts

Discussion

Subscribe to an RSS feed of these comments

Mary Nicole Hicks

Jul 10, 2008 4:54:00 AM

Thank you, this is very helpful. The reason I like the Zend Framework is that there are more people providing snippets of code like this.

But... It would be great if this could similar to the other error decorators for elements and have the UL tag containing the error item have class="errors"

Patrick

Oct 10, 2008 10:13:12 PM

This is great!! How can I make calls to have the form elements and the form error messages to display in a .phtml file. I am new to zend and php. Please be specific.

Rudy

Jan 25, 2010 1:29:46 PM

Hello,

My name is Rudy and after examining your website, I believe I can help you improve your Search Engine Rankings.

Do you wish you could increase your online leads? Getting much more traffic from search engines?
It is much easier and more cost-effective than you might think. We have helped a lot of businesses thrive in this market and we can help you!

If you are interested in outranking your competition please visit http://www.seoimprove.net or mail me at rudy@seoimprove.net

Your comment