Обработка форм, валидация ввода

player

Новичок
Обработка форм, валидация ввода

Доброго времени суток!

Чтото мне стал интересен следующии вопрос:

Идет обработка формы, скажем с двумя полями, username и email
Проверяем юзернейм, проходит ли под регулярное выражение, если нет, пишем "invalid username", если да,
проверяем дальше, есть ли такое имя уже в базе. Если да, то пишем "username token". Похожее делаем с мэйлом - проверка по регулярке и на неповторимость.

Если все прошли успешно, вызываем $user->setUsername($username); и $user->setEmail($email); в которых опять же делаем проверку, всетаки модель сама должна проверять входящие параметры.

Итого 4 лишние проверки, 2 из них с обращением к базе, и все для того чтобы отделить 2 типа ошибок друг от друга:
"invalid username|email" & "token username|email"

Вопрос: может быть кто знает как это сделать элегантнее?
 

Духовность™

Продвинутый новичок
Если все прошли успешно, вызываем $user->setUsername($username); и $user->setEmail($email); в которых опять же делаем проверку
может быть кто знает как это сделать элегантнее?
поменять архитектуру, что бы проверки были в одном месте.

всетаки модель сама должна проверять входящие параметры.
а кто сказал, что моделью должен быть один-единственный класс сущности?

Лично у меня можно в $user->setEmail($email); всё что угодно запихнуть, хоть гиг текста =) Валидация у меня - отдельный слой, причем общие методы валидации (проверка на значение, длину строки, корректность email и т.д.) не привязаны конкретно к каким-то моделям:

PHP:
$validator = new Validator_Chain(...);

// создали модель user из POST. 
$user = $user_mapper->createFromPost(...); 

// а теперь валидация 

// общий метод валидации
$validator->add('user_login', new Module_Common_Validator_VarEmpty($user->getLogin()));
// общий метод валидации
$validator->add('user_login', new Module_Common_Validator_StringLength($user->getLogin(), 30));
// общий метод валидации
$validator->add('user_login', new Module_Common_Validator_CharPassword($user->getLogin()));
// частный метод валидации для объекта User 
// тут лезем в базу с проверкой на наличие пользователя с логином $user->getLogin() и ID != $user->getId()
$validator->add('user_login', new Module_User_Validator_UserLoginExists($user->getLogin(), $user->getId()));

if ($err = $validator->getErrors())
{
     print_r($err['user_login']);
Можно, конечно, валидацию в модель (класс User) вынести, но тогда туда же придется вынести и валидацию с лазаньем в СУБД. В конце-концов получится, что setEmail() должен сделать кучу валидаций и возвратить массив ошибок в случае ошибочных ситуаций. Мне такие модели не нравятся.

* не воспринимай это как совет, я просто рассказал =)
 

craz

Нестандартное звание
Автор оригинала: triumvirat
поменять архитектуру, что бы проверки были в одном месте.

а кто сказал, что моделью должен быть один-единственный класс сущности?

Лично у меня можно в $user->setEmail($email); всё что угодно запихнуть, хоть гиг текста =) Валидация у меня - отдельный слой, причем общие методы валидации (проверка на значение, длину строки, корректность email и т.д.) не привязаны конкретно к каким-то моделям:

PHP:
$validator = new Validator_Chain(...);

// создали модель user из POST. 
$user = $user_mapper->createFromPost(...); 

// а теперь валидация 

// общий метод валидации
$validator->add('user_login', new Module_Common_Validator_VarEmpty($user->getLogin()));
// общий метод валидации
$validator->add('user_login', new Module_Common_Validator_StringLength($user->getLogin(), 30));
// общий метод валидации
$validator->add('user_login', new Module_Common_Validator_CharPassword($user->getLogin()));
// частный метод валидации для объекта User 
// тут лезем в базу с проверкой на наличие пользователя с логином $user->getLogin() и ID != $user->getId()
$validator->add('user_login', new Module_User_Validator_UserLoginExists($user->getLogin(), $user->getId()));

if ($err = $validator->getErrors())
{
     print_r($err['user_login']);
Можно, конечно, валидацию в модель (класс User) вынести, но тогда туда же придется вынести и валидацию с лазаньем в СУБД. В конце-концов получится, что setEmail() должен сделать кучу валидаций и возвратить массив ошибок в случае ошибочных ситуаций. Мне такие модели не нравятся.

* не воспринимай это как совет, я просто рассказал =)
не красиво(((
PHP:
$validator->addValidators('user_login', 
        array( 
            new Module_Common_Validator_VarEmpty($user->getLogin()),
            new Module_Common_Validator_StringLength($user->getLogin(), 30),
            new Module_Common_Validator_CharPassword($user->getLogin()),
            new Module_User_Validator_UserLoginExists($user->getLogin(), $user->getId()),
            )
        );
вот так бы, было бы в разы кашернее)
а еще лучше
если вот так

PHP:
$validator->addValidators('user_login',$user->getLogin() , 
        array( 
            "VarEmpty",
            "StringLength"=>array(0,30),
            "CharPassword",
            "UserLoginExists"=>array($user->getId()),
            )
        );
ну вы поняли к чему я)))
 

player

Новичок
Лично у меня можно в $user->setEmail($email); всё что угодно запихнуть, хоть гиг текста =)
Ну гиг текста везде можно запихнуть, главное какого)))
// создали модель user из POST.
$user = $user_mapper->createFromPost(...);
То есть опять же, создаем юзера не проверяя данных в нем самом? А делаем это както вне объекта... Мне это не очень нравится

Другой вариант: впрыскивание валидатора в модель. Но в этом случае нужно у модели спрашивать, были ли ошибки при валидации, то есть делать в модели функцию с обращением к его валидатору за ошибками. Чтото я недопонимаю
 

whirlwind

TDD infected, paranoid
Автор оригинала: player

Другой вариант: впрыскивание валидатора в модель. Но в этом случае нужно у модели спрашивать, были ли ошибки при валидации, то есть делать в модели функцию с обращением к его валидатору за ошибками. Чтото я недопонимаю
Это хороший вариант. Отлавливать с помощью обсервера. http://pastebin.com/iF6dSj9T
 

player

Новичок
Отлавливать с помощью обсервера. http://pastebin.com/iF6dSj9T
Неплохая стратегия.
Только не до конца понимаю параметры в
function on($eventId,$callback,$context=null,$addArg=null)
function un($eventId,$callback,$context=null,$addArg=null)

И еще: В fireEvent мы также обновляем обсерверов? Не совсем понятно из контекста
 

whirlwind

TDD infected, paranoid
http://pastebin.com/Msbb9A0g

-~{}~ 22.07.10 15:39:

примеры как подписываться
PHP:
$modelObject->on('invalidate', 'onInvalidateInMyForm', $controller, 'something else');
$modelObject->on('modified', 'myfunction');
$modelObject->on('modified', 'myfunction', null, 'form 1');
-~{}~ 22.07.10 15:43:

PS. обсервер вообще великая вещь для написания кода с низкой связанностью
 

crocodile2u

http://vbolshov.org.ru
triumvirat
Затем, что это удобно. Это гарантия того, что валидация проходит всякий раз, когда это необходимо (по крайней мере перед сохранением объекта в БД).
 

A1x

Новичок
а мне нравится как сделали в kohana 3 - http://kohanaframework.org/guide/security.validation

за исключением что я думаю вынести все что связано с валидацией из модели в отдельный слой, наследующий класс Kohana_Validate

Validate extends Kohana_Validate - общие фильтры: XSS, trim, etc.
Validate_User extends Validate - содержит правила валидации для полей модели User

для отдельных форм
Validate_User_Login extends Validate_User
Validate_User_Register extends Validate_User
...
 

A1x

Новичок
User перед сохранением запустил валидацию. Если что не так - сохранения не будет.
валидация обычно нужна при получении данных от пользователя, но не при каждом сохранении. Зачем все мешать в кучу?

-~{}~ 17.08.10 17:57:

Ой, вот не упоминайте о фильтрах XSS, чушь какая-то.
почему чушь? я так понимаю эта штука должна вырезать теги <script> и еще там чего-то делать с входными данными перед тем как пропустить их дальше...

видимо потому что XSS это следствие, а не причина.
а что причина?
 

whirlwind

TDD infected, paranoid
валидация обычно нужна при получении данных от пользователя, но не при каждом сохранении. Зачем все мешать в кучу?
Потому что ограничение например длины параметра это и есть ограничение модели, как высота дома на чертеже или любой другой параметр модели. А контроллер - это частный случай использования модели, который может никогда не возникнуть.
 

crocodile2u

http://vbolshov.org.ru
A1x
Данные могут прийти в виде CSV или XML. Формы для редактирования одних и тех же данных могут быть в разных местах (но при этом содержать одни и те же поля (или даже хуже - одни и те же данные, но поля могут быть названы по-разному)). В этих случаях тоже надо проверять. И проверять точно так же. И когда валидация проводится непосредственно в модели - это избавляет от какого-то количества головной боли.
 

A1x

Новичок
для одной и той же модели в разных частных случаях могут понадобиться немного разные правила валидации
например для логина - только логин, пароль (причем в этом случае даже нет save),
для регистрации - плюс подтверждение пароля и еще какие-то поля
для импорта юзера из CSV или XML - то же самое, но без подтверждения пароля, и т.д.

держать все эти правила в модели имхо слишком ее (модель) загромождает
 

MiksIr

miksir@home:~$
Автор оригинала: A1x
для одной и той же модели в разных частных случаях могут понадобиться немного разные правила валидации
например для логина - только логин, пароль (причем в этом случае даже нет save),
для регистрации - плюс подтверждение пароля и еще какие-то поля
для импорта юзера из CSV или XML - то же самое, но без подтверждения пароля, и т.д.

держать все эти правила в модели имхо слишком ее (модель) загромождает
Валидация в модели имеет отношение к данным _модели_ при сохранении онных данных. Сначала нужно это понять.
Авторизация, импорт и т.д. не имеет отношение к валидации в модели, это будут отдельно реализованные методы.
Регистрация - имеет, но, кстати, такая вещь как подтверждение пароля т.к. не имеет отношение к данным модели - остается в контроллере, а вот правильность написания этого пароля (например, если нельзя русские бувы) - идет уже в модель.
 
Сверху