Question

I have an element. I want to add a custom validator and custom filter to it. The validator makes sure the input is one of several permitted values, then the filter adds some custom values to the input. This means I have to validate the original input first before running the filter. I do it in this order

$element = new Zend_Form_Element_Text('element');
$element->addValidator('PermittedValue', false);
$element->addFilter('TotalHyphen', false);
$this->addElement($element);

but this order isn't being respected. The filter runs first and changes the data, then the validator runs on the filtered data which means it always fails even for valid input. It seems from documentation that this is intentional

Note: Validation Operates On Filtered Values Zend_Form_Element::isValid() filters values through the provided filter chain prior to validation. See the Filters section for more information.

How can I specify the order in which validators and filters run?

Was it helpful?

Solution

Sure seems like creating a custom element that supports post-validation filtering would be the way to go. How about this:

/**
 * An element that supports post-validation filtering
 */
class My_Form_Element_PostValidateFilterable extends Zend_Form_Element_Text
{
    protected $_postValidateFilters = array();

    public function setPostValidateFilters(array $filters)
    {
        $this->_postValidateFilters = $filters;
        return $this;
    }

    public function getPostValidateFilters()
    {
        return $this->_postValidateFilters;
    }

    public function isValid($value, $context = null)
    {
        $isValid = parent::isValid($value, $context);
        if ($isValid){
            foreach ($this->getPostValidateFilters() as $filter){
                $value = $filter->filter($value);
            }
            $this->setValue($value);
        }
        return $isValid;
    }
}

Usage would be something like this:

$elt = $form->addElement('PostValidateFilterable', 'myElement', array(
     'label' => 'MyLabel',
     'filters' => array(
         'StringTrim',
         // etc
     ),
     'validators' => array(
         'NotEmpty',
         // etc 
     ),
     // here comes the good stuff
     'postValidateFilters' => array(
         new My_Filter_RunAfterValidateOne(),
         new My_Filter_RunAfterValidateTwo(),
     ),
));

This keeps the validation and filtering in the form - keeping the controller thin.

Not tested, just a stab in the dark. And surely you could fatten/modify the API to add/remove filters by key, etc.

Whaddya think?

OTHER TIPS

Maybe don't add the filter at all. Validate the content first in the controller, and then use the filter separately:

$request = $this->getRequest();
if ($request->isPost() && $form->isValid($request->getParams())) {
    $filter = new Filter_Whatever();
    $val = $filter->filter($request->getParam('element'));
    ... //call your model or whatever
}

I've never done this, but I suppose this (or something similar) might work.

Good point ! ,

AFAIK filters should or must run before validating the input : from ZF docs

It's often useful and/or necessary to perform some normalization on input prior to validation. For example, you may want to strip out all HTML, but run your validations on what remains to ensure the submission is valid. Or you may want to trim empty space surrounding input so that a StringLength validator will use the correct length of the input without counting leading or trailing whitespace characters.

but if and only if you are in case which can't solve mingos's answer must be the help

What you want to achieve is to change default behavior of how text element is being processed. Thus, I think you could create your own element (e.g. My_Form_Element_Text) that extends Zend_Form_Element_Text and overload its isValid() method.

Specifically you could just change second line in the orginal isValid() method, from $value = $this->getValue(); into $value = $this->getUnfilteredValue();. This way your validation will be performed using unfiltered values.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top