Андрей Друченко
Валидация форм в CakePHP 1.2
В этой статье показывается пример организации валидации типовых форм (типа login / register ) с помощью новой схемы валидации в MVC фреймворке CakePHP 1.2В тексте мало слов и много кода, некоторые куски которого можно просто скопировать (CSS) а в некоторые придется вникнуть, для лучшего понимания того как работает вся система валидации в связке Model-View-Controller.
Как мы уже знаем из документации, в Cake 1.2 значительно поменялся механизм валидации форм — делать сложную и комплексную валидацию стало проще и веселей.
Перед тем как мы начнем рассматривать примеры, сделаем предварительный файл стилей, специально для форм и для тех стандартных вещей в верстке, которые кейковские хелперы делают за нас.
Итак, вот приблизительный пример рабочего CSS
/* a special style for displaying forms */
form { border:1px solid #E3EEF7;float:left;}
div.input {padding:5px;border-bottom:1px solid #DAEBD3;
clear:both;float:left;min-width:430px;width:430px;}
/* for highlighting */
div.required { background-color:#EDFFE5;}
div.input label { display:blockwidth:10em;padding:2px;font-weight:bold;float:left;}
/* tips for field */
div.input .tip-message { max-width:305px;width:305px;clear:both;padding-left:125px;
padding-bottom:2px;font-size:90%;color:#999;display:block;}
/*error messages */
div.input div.error-message { max-width:305px;width:305px;clear:both;
padding-left:125px;padding-top:2px;font-size:90%;color:#990000;}
div.input_checkbox div.error-message { clear:both;padding-left:5px;
padding-top:2px;font-size:90%;color:#990000;}
/* default style for <input type="text" />, <textarea/> */
div.input input, div.input textarea {border:1px solid #9BA3A9;padding:2px;font-family:Arial, Verdana;
font-size:110%;min-width:300px;width:300px;float:left;}
div.input_checkbox {padding:5px 5px 5px 10.3em;border-bottom:1px solid #DAEBD3;
min-width:310px;width:310px;float:left;clear:both;
}
div.input .checkbox width:20pxheight:20px;display:block;float:right;}
div.submit {padding:5px 5px 5px 10.3em;background-color:#FFF9BF;
min-width:310px;width:310px;float:left;clear:both;}
div.submit input {padding:5px;}
/* error highlighting */
div.input input.form-error, div.input textarea.form-error { background-color:#F7BFBE;}
fieldset legend {font-weight:bold;}
В этом файле написана стилизация под верстку которую нам выдают новые кейковские хелперы.
Рассмотрим простой
Пример формы для логина
<h2> Login </h2>
<?=$form->create('User', array('action'=>'login'))?>
<?php echo $form->input('User.email', array(
'size' => '30',
'label'=>'Email:',
'before' => '<div class="tip-message"> '.__('Enter your email or login', true).'</div>',
'error' => 'Invalid e-mail/password')) ?>
<?php echo $form->input('User.passwd', array('size' => '30', 'label' => 'Password:')) ?>
<?=$form->end('Login')?>
С вышеприведенным стилем, наша форма будет выглядеть примерно так:
В результате рендеринга, в ответ мы получим такой xhtml:
<h2> Login </h2> <form id="UserLoginForm" method="post" action="/users/login"> <fieldset style="display:none;"> <input type="hidden" name="_method" value="POST" /> </fieldset> <div class="input required"> <div class="tip-message"> Enter your email or login</div> <label for="UserEmail">Email:</label> <input name="data[User][email]" type="text" size="30" maxlength="200" value="" id="UserEmail" /> </div> <div class="input required"> <label for="UserPasswd">Password:</label> <input type="password" name="data[User][passwd]" size="30" value="" id="UserPasswd" /> </div> <div class="submit"> <input type="submit" value="Login" /> </div> </form>
Для того чтобы лучше понять что же происходит с результирующим html при вводе невалидных данных, вглянем также на другой вариант html-а, но уже с ошибками при валидации:
<h2> Login </h2> <form id="UserLoginForm" method="post" action="/users/login"> <fieldset style="display:none;"><input type="hidden" name="_method" value="POST" /></fieldset> <div class="input required"> <div class="tip-message"> Enter your email or login</div> <label for="UserEmail">Email:</label> <input name="data[User][email]" type="text" size="30" maxlength="200" value="" id="UserEmail" class="form-error" /> <div class="error-message">Invalid e-mail/password</div> </div> <div class="input required"> <label for="UserPasswd">Password:</label> <input type="password" name="data[User][passwd]" size="30" value="" id="UserPasswd" /> </div> <div class="submit"> <input type="submit" value="Login" /> </div> </form>
Как можно заметить из вышеприведенного кода, кейковский хелпер добавил нам class="form-error" на поле ввода которое не прошло валидацию, а также вывел дополнительный <div> с указанием ошибки. Все это отображается в таком виде:
Модель
Перейдем к модели, на которую наша форма завязана и сразу заметим, что приведенный выше пример с явным указанием сообщения об ошибки в хелпере является не очень удачной практикой,по той простой причине, что на одно и то же поле у вас может быть сразу несколько сообщений. В нашем примере для поля username — таких как минимум два:
- Такой username уже существует
- username не проходит регулярное выражение (например /^[a-z0–9][a-z0–9.-_]{2,18}$/ )
Итак, модель Users, которая отвечает за хранение пользовательских логинов и паролей.
<?php
class User extends AppModel {
var $name = 'User';
var $validate = array(
//username
'username' => array(
'custom1' => array(
'rule' => array('custom', "/^[a-z0-9][a-z0-9.-_]{2,18}$/i") ,
'allowEmpty' => false,
'message' => 'Username can contain only a-z,0-9, "-",
"_" & "." and cannot be more then 18 characters',
),
'custom2' => array(
'rule' => array('checkUnique', 'username') ,
'message' => 'This username is already registered. Try other.',
)
),
// password
'password' => array(
'custom1' => array(
'rule' => array('custom', "/^[a-z0-9][a-z0-9.-_]{2,50}$/i") ,
'allowEmpty' => false,
'message' => 'Password can contain only a-z,0-9, "-", "_" & "." and
cannot be more then 50 characters and should start with a letter or number'
) ,
'custom2' => array(
'rule' => array('checkSamePassword') ,
'message' => 'Passwords must be the same!'
),
) ,
// email
'email' => array(
'custom1' => array(
'rule' => array('checkUnique', 'email'),
'message' => 'This email is already registered.'
),
'custom2' => array(
'rule' => array('custom', VALID_EMAIL) ,
'allowEmpty' => false,
'message' => 'Input valid email'
)
),
'first_name' => array('rule' => VALID_NOT_EMPTY,
'message' => 'Cannot be left empty'),
'last_name' => array('rule' => VALID_NOT_EMPTY,
'message' => 'Cannot be left empty')
);
/* i18n
_('Cannot be left empty')
_('Username can contain only a-z,0-9, "-", "_" & "."
and cannot be more then 18 characters')
_('Password can contain only a-z,0-9, "-", "_" & "." and cannot be more
then 50 characters and should start with a letter or number')
_('Passwords must be the same!')
*/
/**
* Cake's callback
*
* @return true/false
*/
function beforeSave() {
if ($this->data['User']['password'] != '') {
$this->data['User']['password'] = md5($this->data['User']['password']);
} else {
unset($this->data['User']['password']);
}
return true;
}
/**
* Check identity of the password and the password_again fields
*
* @param array $data
* @return boolean
*/
public function checkSamePassword($data) {
return $this->data['User']['password'] == $this->data['User']['password_again'];
}
/**
* check Unique fields
*
* @param array $data
* @param string $fieldName
* @return unknown
*/
public function checkUnique($data, $fieldName) {
$valid = false;
if (isset($fieldName) && $this->hasField($fieldName)) {
$valid = $this->isUnique(array(
$fieldName => $this->data['User'][$fieldName]
));
}
return $valid;
}
}
?>
Рассмотрим код модели подробней.
Как можно заметить, сообщения об ошибках на определенные поля больше подходят для формы регистрации, нежели логина. Потенциальному хакеру совсем необязательно выводить информацию о том какие у нас ограничения на пароль, и есть ли у нас уже такой логин в базе.
Поэтому, далее, будем предполагать что у нас форма регистрации, а не логина.
Рассмотрим детальней описание валидации для поля User.username:
<?php
'username' => array(
'custom1' => array(
'rule' => array('custom', "/^[a-z0-9][a-z0-9.-_]{2,18}$/i") ,
'allowEmpty' => false,
'message' => 'Username can contain only a-z,0-9, "-",
"_" & "." and cannot be more then 18 characters',
),
'custom2' => array(
'rule' => array('checkUnique', 'username') ,
'message' => 'This username is already registered. Try other.',
)
)
?>
На это поле у нас задано два правила для валидации custom1 и custom2. Имена для этих правил можно выбирать произвольно, для Кейка — это всего лишь ключи.
На каждое правило задается метод валидации, в первом — это проверка по пользовательскому регулярному выражению, во втором — это вызов callback функции Users::checkUnique(), с вторым параметром равным username.
Правила выполняются в том порядке в котором записаны в модели. То есть, если сработало правило custom1, и за ним custom2 — то в результирующей форме вы увидите сообщение об ошибке This username is already registered. Try other.
Заметим также что записи типа
<?php
'first_name' => array('rule' => VALID_NOT_EMPTY,
'message' => 'Cannot be left empty'),
?>
и
<?php
'first_name' => array(
'custom1' => array(
'rule' => array('custom', VALID_NOT_EMPTY) ,
'message' => 'Cannot be left empty'
))
?>
будут эквивалентны по результату.
В Cake 1.2 существует целое множество встроенных проверок для стандартных случаев, среди них такие
- alphaNumeric — буквыцифры
- between — проверка на длину (от 5 до 10 символов например)
- blank — пустое поле
- cc — проверка кредиток
- comparison — больше/меньше чего либо
- date — дата
- decimal — числа с плавающей точкой
- email — электропочта
- ip — IP адрес
- minLength — проверка на минимальную длину
- maxLength — проверка на максимальную длину
- numeric — что-то цельночисловое
- phone — телефон (пока что только US )
- postal — ZIP код, или почтовый код (US/Canada/UK)
- ssn — социальный номер страхования (US only)
- url — Unified Resource Locator
Для более детального разбирательства с тем что можно описывать в полях типа custom нужно глянуть в раздел по Валидации данных на CakePHP
Теперь рассмотрим валидацию со стороны контроллера
Контроллер
Рассмотрим вариант с формой логина
<?php
class UsersController extends AppController {
var $name = 'Users';
var $uses = array('User');
var $components = array('obAuth')
/**
* Action for user login
*
*/
function login() {
if (isset($this->data['User'])) {
// login using obAuth component
if($this->obAuth->login($this->data['User'])) {
$this->redirect('/users/index');
} else {
// invalidate form
$this->User->invalidate('email');
}
}
}
/**
* Creates new user
*
*/
function register() {
if (!empty($this->data)) {
$data = $this->data;
if ($this->User->save($data)) {
$this->redirect('/pages/welcome');
}
}
}
}
?>
В данном примере используется сторонний компонент для авторизации и аутентификации obAuth
Итого, у нас два действия:
- login — для обработки формы логина
- register — для добавления нового пользователя
Обратим внимание на функцию User->invalidate() которая занимается только тем что помечает поле email как непрошедшее валидацию, а значит включает отображение ошибок именно на это поле.
Для логина, нам достаточно выводить ошибку только на одно поле, в нашем конкрентом примере — email.
Что до действия register — то оно довольно стандартное, после удачной валидации и сохранения происходит редирект на страницу welcome.
Материалы по теме
Cake 1.2 Data validation from the official manualValidation with CakePHP 1.2
All about validation in CakePHP 1.2
Компонент obAuth
Последние комментарии:
Обсудить (комментариев: 0)