Валидация в Yii

в 20:46, , рубрики: yii, yii framework, валидация, валидация данных, валидация форм, метки: , , , ,

Доброго времени суток. Сегодня мне хотелось бы разобрать такую интересную возможность Yii Framework, как валидация данных моделей. На момент написания статьи актуальная версия фреймворка 1.1.10, собственно рассматривать валидацию на ней и будем.
Хочу сразу сказать, что мне не хочется перепечатывать мануалы и API, поэтому я по возможности буду ссылаться на готовые источники. Кроме того, я не буду описывать то, как пользоваться валидаторами. Я постараюсь раскрыть механизм валидации моделей Yii на основе правил валидации, чтобы используя их вы понимали что же на самом деле происходит и где в случае чего можно искать ошибки.

Начало понимания валидаторов

Итак, на замечательном сайте yiiframewrok.ru имеется раздел рецептов.
В данном разделе есть Краткий справочник по валидации. Оригинал данной статьи находится по данному адресу.
Прочитав данную статью вы поймете:

  1. Как задавать правила валидации
  2. Что представляет из себя конкретное правило
  3. Узнаете список стандартных валидаторов и их параметров

Далее стоит прочесть статью Создание модели (Оригинал) из которой вы узнаете:

  1. Кое что о сценариях. (Хочу подчеркнуть этот момент, прдокументирован он слабо)
  2. Как создавать валидаторы
  3. Как выполнять валидацию

Кстате говоря, примеры создания своих валидаторов хорошо описаны в книге: Yii 1.1 Application Development Cookbook
Вооружившись полученными знаниями мы можем перейти дальше и разобраться, что же происходит при вызове:CModel::validate();

Вглубь CModel::validate();

Метод CModel::validate($attributes=null, $clearErrors=true); — принимает на вход 2 необязательных параметра (список аттрибутов для валдиации, и ключ clearErrors, который производит очистку массива с ошибками перед вызовом валидаторов)
Это важно понимать если вы будете расширять стандартные модели Yii и использовать метод CModel::addError(); для своих целей, т.к. CModel::validate(); возвращает true в случае успешной валидации данных и false если CModel::hasErrors(); вернет true.
Кроме того здесь нужно отметить, что вадилация не будет работать если вы переопределили метод CModel::beforeValidate(); и он вернул false.
Дабы разбавить текст, давайте взглянем на код и все станет более менее понятно:

  1. public function validate($attributes=null, $clearErrors=true)
  2. {
  3.     if($clearErrors)
  4.         $this->clearErrors();
  5.     if($this->beforeValidate())
  6.     {
  7.         foreach($this->getValidators() as $validator)
  8.             $validator->validate($this,$attributes);
  9.         $this->afterValidate();
  10.         return !$this->hasErrors();
  11.     }
  12.     else
  13.         return false;
  14. }

Я думаю, здесь вопросов больше не возникает, так что давайте лучше заглянем в метод CModel::getValidator()

foreach($this->getValidators() as $validator)

чтобы понять, как Yii получает список валидаторов исходя из правил указанных в методе CModel::rules()

CModel::getValidators()

  1.     public function getValidators( $attribute = null )
  2.     {
  3.         if ( $this->_validators === null )
  4.             $this->_validators = $this->createValidators();
  5.  
  6.         $validators = array( );
  7.         $scenario = $this->getScenario();
  8.         foreach ( $this->_validators as $validator )
  9.         {
  10.             if ( $validator->applyTo( $scenario ) ) {
  11.                 if ( $attribute === null || in_array( $attribute, $validator->attributes, true ) )
  12.                     $validators[] = $validator;
  13.             }
  14.         }
  15.         return $validators;
  16.     }

Метод возвращает массив валидаторов описанных правилами в модели.
Интересный момент здесь начинается с 7ой строки. Выше я упоминал о сценариях. Так вот, здесь мы видим, что валидатор добавляется к цепочке, только в том случае если метод CValidator::applyTo($scenario);
вернет true, а true он вернет либо в случае, когда параметр «on» не задан либо в том случае когда валидатор относится к сценарию в котором выполняется модель.
Идем дальше. Рассморим метод CModel:createValidators();

$this->_validators = $this->createValidators();

который в сути будет являся нижним и последним уровнем разбора правил валидации, а далее подведем некоторые итоги.

Ближе к завершению: CModel::createValidators()

  1.     public function createValidators()
  2.     {
  3.         $validators = new CList;
  4.         foreach ( $this->rules() as $rule )
  5.         {
  6.             if ( isset( $rule[0], $rule[1] ) )  // attributes, validator name
  7.                 $validators->add( CValidator::createValidator( $rule[1], $this, $rule[0], array_slice( $rule, 2 ) ) );
  8.             else
  9.                 throw new CException( Yii::t( 'yii', '{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.', array( '{class}' => get_class( $this ) ) ) );
  10.         }
  11.         return $validators;
  12.     }

Вот мы и дошли до нижнего уровня. Данный метод как раз таки и производит разбор тех самых правил валидации описанных в CModel::rules() и при помощи статического метода класса CValidator::createValidator() создает объекты валидаторов описанные данными правилами.
Посмотре API метода CValidator::createValidator() а так же вспомнив про стандартные параметры правила валидации

  1. array(
  2.     'список полей модели',
  3.     'валидатор',
  4.     'on'=>'имя сценария',
  5.     'message'=>'сообщение об ошибке',
  6.     …параметры валидации…
  7. );

, все сразу встанет на свои места.

Подведем итоги

Как видите алгоритм разбора правил валидации не такая уж и сложная вещь. Главное иметь желание разобраться и понять проблему. При понимании того как работает код отпадает куча разнообразных вопросов возникающих при разработке, когда что то идет не так и программа выполняется не так как нужно.

В завершении можем обобщить некоторые особенности которые позволят понять почему возможно ваш валидатор работает не так как хотелось бы или же почему какое либо правило валидации не выполняется.

  • При вызове метода CModel::validate() происходит проверка выполнен ли метод CModel::beforeValidate().
    Если метод не выполнен валидация считается не пройденной
  • Если метод CModel::hasErrors() вернул true валидация считается не пройденной
  • При вызове метода CModel::getValidators() добавляются только валидаторы которые относятся к текущему сценарию а так же валидаторы для которых в правилах валидации сценарий не указан

Автор: DexterHD

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js