4
votes

I have an Multiselect Zend Form element with many options. I have to validate the number of selected options (at least N options and at most M options are selected). I want the error message to be printed in the form just like regular Zend Validate error message.

What is the simplest (and the least hacky) way to do this?

A regular validator can't do this, because each selected value is validated completely individually.

I tried to override the form's isValid method and added the logic there (return false and add an error message if the number is outside the allowed range), but that results in the error message being printed multiple times (for each selected value). I feel that trying to fix this would result in extremely hacky code.

Thanks for help

2

2 Answers

1
votes

Don't know if this is too hacky for you.

$element = new Zend_Form_Element_Multiselect('CheckThis');
$options = array(
    1 => 'Option One',
    2 => 'Option Two',
    3 => 'Option Three',
    4 => 'Option Four',
    5 => 'Option Five',
    6 => 'Option Six',
    7 => 'Option Seven',
    8 => 'Option Eight',
);
$element->addMultiOptions($options);
$betweenOptions   = array('min' => 2, 'max' => 4);
$betweenValidator = new Zend_Validate_Between($betweenOptions);
$betweenValidator->setMessage("The number of submitted values '%value%' is not between '%min%' and '%max%', inclusively",'notBetween');
if ( true === $this->getRequest()->isPost() ) {
    if ( true === $betweenValidator->isValid(count($_POST['CheckThis'])) ) {
        $form->isValid($_POST);
    } else {
        $messages = $betweenValidator->getMessages();
        $element->addError($messages['notBetween']);
        $form->setDefaults($_POST);                
    }
}

UPDATE
Note to avoid the duplicate error messages.
If you can don't call isValid on the form or element; like in my example where I only add error message and set defaults. The problem is that isValid($value) will call _getErrorMessages() and that method checks the error messages against the values.

If you cannot avoid a call to isValid I would extend the Multiselect element and override that _getErrorMessages() method with my one logic. You can find that method in the Zend/Form/Element.php class all the way at the bottom.

0
votes

I decided to create my custom Errors element decorator, which discards non-unique error messages:

<?php
class Element_Decorator_Errors extends Zend_Form_Decorator_Abstract
{
    /**
     * Render errors
     * 
     * @param  string $content 
     * @return string
     */
    public function render($content)
    {
        $element = $this->getElement();
        $view    = $element->getView();
        if (null === $view) {
            return $content;
        }

        // The array_unique is the only difference in comparison to the default Error decorator   
        $errors = array_unique($element->getMessages());
        if (empty($errors)) {
            return $content;
        }

        $separator = $this->getSeparator();
        $placement = $this->getPlacement();
        $errors    = $view->formErrors($errors, $this->getOptions()); 

        switch ($placement) {
            case self::APPEND:
                return $content . $separator . $errors;
            case self::PREPEND:
                return $errors . $separator . $content;
        }
    }
}
?>