Как заставить этот фильтр работать после этого валидатора

У меня есть элемент. Я хочу добавить к нему настраиваемый валидатор и настраиваемый фильтр. Валидатор проверяет, является ли ввод одним из нескольких разрешенных значений, затем фильтр добавляет к вводу некоторые пользовательские значения. Это означает, что я должен сначала проверить исходный ввод, прежде чем запускать фильтр. делаю в таком порядке

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

но этот порядок не соблюдается. Фильтр запускается первым и изменяет данные, затем валидатор работает с отфильтрованными данными, что означает, что он всегда терпит неудачу даже для действительного ввода. Из документации следует, что это сделано намеренно

Примечание. Проверка работает с отфильтрованными значениями. Zend_Form_Element::isValid() фильтрует значения через предоставленную цепочку фильтров перед проверкой. Дополнительную информацию см. в разделе «Фильтры».

Как указать порядок запуска валидаторов и фильтров?


person jblue    schedule 28.01.2011    source источник


Ответы (4)


Конечно, похоже, что создание пользовательского элемента, который поддерживает фильтрацию после проверки, было бы правильным решением. Как насчет этого:

/**
 * 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;
    }
}

Использование будет примерно таким:

$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(),
     ),
));

Это сохраняет проверку и фильтрацию в форме, сохраняя тонкий контроллер.

Не проверено, просто удар в темноте. И наверняка можно было бы жирнее/модифицировать API, добавлять/удалять фильтры по ключу и т.п.

Что думаешь?

person David Weinraub    schedule 28.01.2011
comment
+1. Выглядит очень красиво. Я также не знал, что метод setPostValidateFilters() будет вызываться автоматически (да?) для ключа postValidateFilters. - person Marcin; 28.01.2011
comment
@Marcin: я думаю, что так работают опции. Если есть метод, который соответствует названию параметра, то этот метод вызывается во время setOptions(). - person David Weinraub; 28.01.2011

Может быть, вообще не добавлять фильтр. Сначала проверьте содержимое в контроллере, а затем используйте фильтр отдельно:

$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
}

Я никогда этого не делал, но полагаю, что это (или что-то подобное) может сработать.

person mingos    schedule 28.01.2011
comment
+1 Это возможное решение, но не уверен, что оно мне нравится. Я сделаю это, если ничего не поможет, но я бы предпочел сохранить тонкий контроллер и сохранить логику фильтрации с элементом формы, которому он принадлежит. - person jblue; 28.01.2011
comment
Конечно, я бы предпочел сохранить его и в коде формы, но я не могу придумать никакого другого способа повлиять на порядок вызова валидаторов и фильтров. Я также иногда добавляю в контроллер вещи, которые в идеале должны находиться в форме, например, когда набор полей требуется тогда и только тогда, когда что-то еще проверяется/заполняется. Вы не можете надежно проверить это на стороне клиента или до того, как пользователь заполнит форму, поэтому иногда необходимо выполнить некоторую дополнительную магию формы в контроллере, нравится вам это или нет :(. - person mingos; 28.01.2011

Хорошая точка зрения ! ,

Фильтры AFAIK должны или должны запускаться перед проверкой ввода: из ZF docs

Часто бывает полезно и/или необходимо выполнить некоторую нормализацию ввода перед проверкой. Например, вы можете захотеть удалить весь HTML-код, но запустить проверку того, что осталось, чтобы убедиться, что отправка действительна. Или вы можете захотеть обрезать пустое пространство вокруг ввода, чтобы валидатор StringLength использовал правильную длину ввода без подсчета начальных или конечных пробельных символов.

но тогда и только тогда, когда вы находитесь в случае, если вы не можете решить, ответ Мингоса должен быть помощью

person tawfekov    schedule 28.01.2011
comment
Я думаю, что это ошибка конструкции. Некоторые фильтры очень сложны и вносят изменения, которые могут сделать ввод недействительным. Итак, фильтры следует разделить на 2 типа. Упомянутые вами фильтры нормализации, такие как обрезка, которую необходимо запустить перед проверкой, и другие фильтры, которые вносят более сложные изменения. Затем разработчик может выбрать при добавлении фильтра, когда активировать этот фильтр. - person jblue; 28.01.2011

Чего вы хотите добиться, так это изменить поведение по умолчанию того, как обрабатывается текстовый элемент. Таким образом, я думаю, вы могли бы создать свой собственный элемент (например, My_Form_Element_Text), который расширяет Zend_Form_Element_Text и перегружает его метод isValid().

В частности, вы можете просто изменить вторую строку в исходном методе isValid() с $value = $this->getValue(); на $value = $this->getUnfilteredValue();. Таким образом, ваша проверка будет выполняться с использованием нефильтрованных значений.

person Marcin    schedule 28.01.2011
comment
Это интересная идея. Единственная проблема, которую я вижу, это то, что я автоматически теряю все остальные фильтры. Если я делаю модификацию нефильтрованного значения, то, например, я теряю фильтр обрезки. По этой причине ответ минго, вероятно, дает мне желаемый эффект, хотя мне не нравится, что он находится в контроллере. Было бы идеально, если бы разработчик мог указать точный порядок валидаторов и фильтров. - person jblue; 28.01.2011
comment
@jblue. Итак, вы хотите, чтобы после проверки применялся только один фильтр, а остальные — как обычно, да? Это, конечно, все еще можно сделать, создав собственный текстовый элемент. - person Marcin; 28.01.2011