Доброго времени суток. Сегодня мне хотелось бы разобрать такую интересную возможность Yii Framework, как валидация данных моделей. На момент написания статьи актуальная версия фреймворка 1.1.10, собственно рассматривать валидацию на ней и будем.
Хочу сразу сказать, что мне не хочется перепечатывать мануалы и API, поэтому я по возможности буду ссылаться на готовые источники. Кроме того, я не буду описывать то, как пользоваться валидаторами. Я постараюсь раскрыть механизм валидации моделей Yii на основе правил валидации, чтобы используя их вы понимали что же на самом деле происходит и где в случае чего можно искать ошибки.
Начало понимания валидаторов
Итак, на замечательном сайте yiiframewrok.ru имеется раздел рецептов.
В данном разделе есть Краткий справочник по валидации. Оригинал данной статьи находится по данному адресу.
Прочитав данную статью вы поймете:
- Как задавать правила валидации
- Что представляет из себя конкретное правило
- Узнаете список стандартных валидаторов и их параметров
Далее стоит прочесть статью Создание модели (Оригинал) из которой вы узнаете:
- Кое что о сценариях. (Хочу подчеркнуть этот момент, прдокументирован он слабо)
- Как создавать валидаторы
- Как выполнять валидацию
Кстате говоря, примеры создания своих валидаторов хорошо описаны в книге: 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.
Дабы разбавить текст, давайте взглянем на код и все станет более менее понятно:
- public function validate($attributes=null, $clearErrors=true)
- {
- if($clearErrors)
- $this->clearErrors();
- if($this->beforeValidate())
- {
- foreach($this->getValidators() as $validator)
- $validator->validate($this,$attributes);
- $this->afterValidate();
- return !$this->hasErrors();
- }
- else
- return false;
- }
Я думаю, здесь вопросов больше не возникает, так что давайте лучше заглянем в метод CModel::getValidator()
foreach($this->getValidators() as $validator)
чтобы понять, как Yii получает список валидаторов исходя из правил указанных в методе CModel::rules()
CModel::getValidators()
- public function getValidators( $attribute = null )
- {
- if ( $this->_validators === null )
- $this->_validators = $this->createValidators();
- $validators = array( );
- $scenario = $this->getScenario();
- foreach ( $this->_validators as $validator )
- {
- if ( $validator->applyTo( $scenario ) ) {
- if ( $attribute === null || in_array( $attribute, $validator->attributes, true ) )
- $validators[] = $validator;
- }
- }
- return $validators;
- }
Метод возвращает массив валидаторов описанных правилами в модели.
Интересный момент здесь начинается с 7ой строки. Выше я упоминал о сценариях. Так вот, здесь мы видим, что валидатор добавляется к цепочке, только в том случае если метод CValidator::applyTo($scenario);
вернет true, а true он вернет либо в случае, когда параметр «on» не задан либо в том случае когда валидатор относится к сценарию в котором выполняется модель.
Идем дальше. Рассморим метод CModel:createValidators();
$this->_validators = $this->createValidators();
который в сути будет являся нижним и последним уровнем разбора правил валидации, а далее подведем некоторые итоги.
Ближе к завершению: CModel::createValidators()
- public function createValidators()
- {
- $validators = new CList;
- foreach ( $this->rules() as $rule )
- {
- if ( isset( $rule[0], $rule[1] ) ) // attributes, validator name
- $validators->add( CValidator::createValidator( $rule[1], $this, $rule[0], array_slice( $rule, 2 ) ) );
- else
- 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 ) ) ) );
- }
- return $validators;
- }
Вот мы и дошли до нижнего уровня. Данный метод как раз таки и производит разбор тех самых правил валидации описанных в CModel::rules()
и при помощи статического метода класса CValidator::createValidator()
создает объекты валидаторов описанные данными правилами.
Посмотре API метода CValidator::createValidator()
а так же вспомнив про стандартные параметры правила валидации
- array(
- 'список полей модели',
- 'валидатор',
- 'on'=>'имя сценария',
- 'message'=>'сообщение об ошибке',
- …параметры валидации…
- );
, все сразу встанет на свои места.
Подведем итоги
Как видите алгоритм разбора правил валидации не такая уж и сложная вещь. Главное иметь желание разобраться и понять проблему. При понимании того как работает код отпадает куча разнообразных вопросов возникающих при разработке, когда что то идет не так и программа выполняется не так как нужно.
В завершении можем обобщить некоторые особенности которые позволят понять почему возможно ваш валидатор работает не так как хотелось бы или же почему какое либо правило валидации не выполняется.
- При вызове метода
CModel::validate()
происходит проверка выполнен ли методCModel::beforeValidate()
.
Если метод не выполнен валидация считается не пройденной - Если метод
CModel::hasErrors()
вернул true валидация считается не пройденной - При вызове метода
CModel::getValidators()
добавляются только валидаторы которые относятся к текущему сценарию а так же валидаторы для которых в правилах валидации сценарий не указан
Автор: DexterHD