phpzend-frameworkzend-validatezend-filter

How to make this Filter run after this Validator


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?


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?