Pull to refresh

Валидация в Yii

Reading time 6 min
Views 34K
Доброго времени суток. Сегодня мне хотелось бы разобрать такую интересную возможность 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() добавляются только валидаторы которые относятся к текущему сценарию а так же валидаторы для которых в правилах валидации сценарий не указан
Tags:
Hubs:
+15
Comments 14
Comments Comments 14

Articles