Zend_Form and Validating Values for Multiple Column DB Keys

I’ve recently started developing my first project fully in Zend Framework and I’ve been loving it so far. There are so many components in the framework that make a developers life easier, among which is the combination of Zend_Filter, Zend_Validate and Zend_Form. I love how easy it is to create and validate forms using these components, although Zend Form decorators are still a black art to me. Today I came across a particular problem in validating values that I thought I would write about here.

Zend_Validate includes two classes to work with database columns: Zend_Validate_Db_NoRecordExists and  Zend_Validate_Db_RecordExists. As their names suggest these check that a value either does or doesn’t exist in a specified column in a database table. The classes work really well in a form when specified as a validator for a value but things can get a little trickier if you need to check two columns in a table. The problem I had was in creating a simple FAQ page for a site. The site owner gets to add FAQs to the site and each FAQ is contained in a category. As a little sanity check I wanted to make sure that the combination of category and question was unique. The database also has a multiple column unique key on these to enforce the integrity. My first attempt looked something like this in a class that extends Zend_Form:

 


public function init() {
//Category options are populated elsewhere.
$categories = new Zend_Form_Element_Select('category');
$categories->setAllowEmpty(FALSE)
	   ->setAttrib('required', 'required')
	   ->setLabel('FAQ Category:')
	   ->addErrorMessage('Please choose a valid category from the list.')
	   ->addMultiOption('', '--Please Choose--');
$question = new Zend_Form_Element_Text('question');
$question->setRequired(TRUE)
	 ->setLabel('Question:')
	 ->setAttrib('required', 'required')
	 ->setAttrib('max', '100')
	 ->addValidator('StringLength', FALSE, array(0,100))
	 ->addErrorMessage('Enter a question of up to 100 characters in length.');
$validate = new Zend_Validate_Db_NoRecordExists(array('table' => 'faqs', 'field' => 'title'));
$validate->getSelect()->where('FaqCategoryId = ?', $this->getValue('category'), Zend_Db::PARAM_INT);
$question->addValidator($validate);
}

I thought that this would add the extra where clause to the select statement for the validation. This failed miserably though. The problem is that the value referenced in ‘$this->getValue(‘category’)’  doesn’t exist when the init() method is called. Values are only added to the form when the isValid() method is called. The solution was to extend the isValid() method and to perform the extra check there.

public function isValid($data) {
	$result = parent::isValid($data);
	if ($result) {
		$validate = new Zend_Validate_Db_NoRecordExists(array('table' => 'faqs', 'field' => 'question'));
		//Add a where clause to check the FaqCategoryId
	        $validate->getSelect()->where('FaqCategoryId = ?', $this->getValue('category'), Zend_Db::PARAM_INT);
	       	$result = $validate->isValid($this->getValue('question'));
	       	if (! $result)
	       		$this->getElement('question')->setErrors(array('The selected category already has an FAQ with that question. Please edit the question.'));
		}
		return $result;
	}

By extending the isValid() method I can call the parent method to do the majority of the work. If the form looks valid I then perform the extra check to make sure the combination of question and category is unique. If it isn’t an error message is set and the validation fails. I’ve also used this in another part of the site where the client is adding news articles to the site. In that instance the combination of published date and title needs to be unique as these are used in the url to uniquely reference the article. The same method in form validation checks that when the form is submitted. Using this method I’ve been able to clean up my controller and move all validation checks into the form, where they should be. I’d love to hear in the comments how other people approach this problem though.

6 thoughts on “Zend_Form and Validating Values for Multiple Column DB Keys

  1. Sir,

    You did not explain the relation between $question->addValidator($validate); and public function isValid($data) . I mean how shall the addValidator($validate) work if $validate is a simple variable in isValid($data). I had the following error

    Invalid validator provided to addValidator; must be string or Zend_Validate_Interface

    public function init()
    {

    $authorname = new Zend_Form_Element_Text(‘authorname’);
    $authorname->setDecorators(array(‘ViewHelper’,’Errors’));
    //$validate = new Zend_Validate_Db_RecordExists(array(‘table’ => ‘albums’, ‘field’ => ‘artist’));
    $authorname ->setRequired(true)
    ->addFilter(‘StripTags’)
    ->addFilter(‘StringTrim’)
    ->addValidator(‘NotEmpty’)
    ->setAttrib(‘size’,20)
    ->addValidator(‘stringLength’, false, array(6, 12))
    ->addvalidator($validate);
    $submit = new Zend_Form_Element_Submit(‘submit’);
    $submit->setAttrib(‘id’, ‘submitbutton’);

    $this->addElements(array($authorname,$submit));

    }

    public function isValid($data) {
    $result = parent::isValid($data);

    if ($result) {
    $validate = new Zend_Validate_Db_RecordExists(array(‘table’ => ‘albums’, ‘field’ => ‘artist’));
    //Add a where clause to check the FaqCategoryId
    $validate->getSelect()->where(‘id = ?’, 6, Zend_Db::PARAM_INT);
    $result = $validate->isValid($this->getValue(‘artist’));
    if (! $result)
    $this->getElement(‘question’)->setErrors(array(‘The selected category already has an FAQ with that question. Please edit the question.’));
    }
    return $result;
    }

    id – column in albums and i am passing 6 (hard coded).

    Kindly clear my error

    Shalom

Leave a Reply