Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 885
jblue Avatar asked Jan 28 '11 00:01

jblue


2 Answers

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?

like image 167
David Weinraub Avatar answered Sep 24 '22 08:09

David Weinraub


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.

like image 32
mingos Avatar answered Sep 24 '22 08:09

mingos