Почему валидация в Zend_Form это плохо, или о том, как делать правильную валидацию
27/08/2009 09:03
В настоящий момент Zend Framework имеет набор валидаторов, которые обычно используются для проверки входных данных из форм. Для этого используется пакет Zend_Form, отдельно же валидаторы используются достаточно редко.
Вроде бы всё хорошо, все пользуются и все довольны. Но если подумать, то в ZF валидация располагается не на том уровне, на котором хотелось бы. Я уверен, что валидация входных данных должна производиться именно в моделях и ни в коем случае не в формах, как сделано на даный момент. Почему? Остановимся на этом подробнее.
Представьте, что у вас есть форма для добавления поста в блог. Соответственно, есть класс, наследуемый от Zend_Form, который определяет, какие поля будут в форме, устанавливает на них валидаторы и, возможно, декораторы. В итоге мы получаем готовую форму, которую достаточно только вывести в нужное место шаблона - и можно использовать. Все будет валидироваться, и вроде бы все прекрасно.
Но представьте, что в один прекрасный момент вам понадобится написать робота, который должен будет опубликовывать посты, приходящие на указанный e-mail. То есть скрипт должен заходить на почтовый ящик через некоторые промежутки времени, получать по некоторым критериям письма, парсить их и опубликовывать в блоге. Получается, что форма тут не нужна - мы добавляем письма напрямую, минуя форму. А вдруг парсинг письма пройдет неправильно? Мы запросто можем получить пустой пост в блоге, или некорректный, если произойдут ошибки при парсинге.
Что делать? Чтобы избежать подобных ситуаций, придется добавлять валидатор и в модель, которая отвечает за добавление поста. Но получается, что теперь валидаторы будут указаны в двух разных местах - в форме и в модели. Это ужасно.
Чтобы избежать подобных противоречий, необходимо определять валидаторы только в конечной точке, отвечающей за непосредственное добавление поста в БД - конечно же в модели, и забыть про использование валидаторов в форме.
Кто-то скажет: так ведь форма не обязательно может добавлять данные в БД, она, например, может отправлять письмо на ту же почту, или делать что-то другое, не работая с БД. Зачем тогда определять валидаторы в модели?
Тут нужно понять, что модель - это не та штука, которая предназначена только для добавления данных в БД. Модель - это бизнес-логика, это то место, где происходит обработка поступивших данных. В Zend Framework мы имеем только один вид модели - Zend_Db_Table, и эта модель работает только с таблицами БД. Мы пока не имеем нечто среднее - модель, которая умеет работать и с таблицей и без ее использования. Когда мы получим такую функциональность, тогда мы сможем определять валидаторы в модели, будь это модель, добавляющая данные в таблицу БД, либо же не имеющая таблицы.
Я не знаю, есть ли какие-то подвижки в сторону “правильных моделей” у команды разработчиков ZF, но я очень надеюсь, что подобные желания у них есть. В любом случае, даже если это не так, я сам возьмусь за написание описанной мною функциональности.
Спасибо, надеюсь вам было интересно :)
Модель в моем понимании это то что не поставляется ни в каком виде фреймворком, это логика приложения. Zend_Db_Table всего лишь предоставляет способ удобного доступа в БД.
А вообще рассуждения весьма интересные, сам об этом думал недавно. Интересно было бы увидеть вариант реализации подобной идеи.
P.S. Добавил ваш блог в каталог.
2 san:
Да, вы правы, модель - это, конечно же, не таблица в БД. Это то “мясо” приложения, которое находится на костях - контроллере (если можно провести такую аналогию). Просто чаще всего, в приложениях которые каждый из нас делает, это наша бизнес-логика завязана в основном на БД. И нам бы нужно какой-то инструмент, чтобы было удобнее работать с подобными вещами - но увы, его пока нет. Приходится обходиться.
Я пока смотрю в сторону Rails - очень уж там удобно сделана работа с моделями. Очень :) Конкретной реализации у меня пока нет, есть только кое какие наброски, килобайт на 30 кода. Тут наверное нужно единомышленников искать - одному это можно годами делать. Потом как-нибудь напишу пост на эту тему.
Спасибо за добавление в каталог :)
“Для этого используется пакет Zend_Form, отдельно же валидаторы используются достаточно редко.”
Ничего не знаю, я использую валидаторы для валидации. формы тут вообще не причем. То что в них можно встроить валидаторы - удобная фича.
“Но представьте, что в один прекрасный момент вам понадобиться написать робота, который должен будет опубликовывать посты, приходящие на указанный e-mail. То есть скрипт должен заходить на почтовый ящик через некоторые промежутки времени, получать по некоторым критериям письма, парсить их и опубликовывать в блоге. Получается, что форма тут не нужна - мы добавляем письма напрямую, минуя форму. А вдруг парсинг письма пройдет неправильно? Мы запросто можем получить пустой пост в блоге, или некорректный, если произойдут ошибки при парсинге.”
Фишка в том, что валидаторы не обязательно должны быть использованны вместе с формой. Вы можете взять валидатор (который сам по себе является моделью реализующей валидирующую часть упомянутой вами бизнес логики) где угодно. В скрипте-роботе, для валидации объекта запроса… ваще где угодно. Таккой подход/вынесение валидации в отдельную модель нравится гораздо больше. Он предоставляет тот же функционал, но более диференцированно, гораздо гибче. Я могу валидировать данные где угодно когда угодно, мне не надо вызывать методы модели Книга чтобы узнать валидны ли какие либо данные.
Конечные же модели должны генерировать исключения в исключительных ситуациях,
Где что я не вижу? Где узкие места?
2 lcf:
Это так. Но я видел что люди его чаще всего используют именно в формах. Наверное, это зависит от специфики проекта.
Мысль понятна. Но так ли это накладно будет вызвать нужные методы у модели, особенно если это было бы сделано удобно? Тем более, где еще нужно валидировать данные, как не в модели? По-моему это и есть та самая точка, где требуется валидация пришедших данных, потому, как после этой точки с данными что-то происходит: они записываются в БД, посылаются на e-mail, или отправляются на дальнейшую обработку куда-либо.
Как подписаться на уведомление о новых комментах? Можно как нить?
Поставил плагин - теперь можно подписываться.
Спасибо.
А еще робот паук может сделать так :)
$data = …;
$form = new App_MyForm();
if($form->isValid($data)) {
…
}
Формы очень удобно агрегируют валидаторы и из них же опять удобно извлечь все ошибки валидаторов.
Плюс есть мнение - что объекты форм тоже можно отнести к части модели
а наиболее острый вопрос не в том где валидировать, а в том КАК узнает клиент (форма) о требуемом формате данных у сервера (модели). потому что валидировать с последующим выбросом exception - это не валидация, а просто sanity check.
вот как сделать так модель, чтобы она не просто валидировала, но и ДЕКЛАРИРОВАЛА свою валидационную составляющую
Смотрите, если я вас правильно понял, то тогда примерно так.
Если делать так, что в модели будут валидироваться входящие данные, то, можно указывать валидаторы наподобие валидаторов в Rails. Соответственно, модель теперь знает, как валидировать свои данные (она хранит определенные валидаторы внутри объекта). Теперь, если мы передадим объект модели в view script, а в нем в какой-либо хелпер вида, который сгенерирует нам javascript-код для валидации формы.
И какой такой формат данных может быть у модели?
Все же, мне кажется, моя твоя непониль :)
Жду ответа.
смотри пример. есть класс Employee, у которого есть свойство age. в итоге имеем $employee->age. у этого свойства есть ряд мета-свойств. а именно:
- диапазон значений
- смысл (что это вообще за свойство)
- условия допустимости (например if ($sex == ‘female’) $age < 60 else $age < 65)
- и куча всего прочего
теперь имеем две формы. разные. в одной мы хотим назначить возраст сотруднику. в другой мы хотим этот возраст редактировать на основании чего-то (допустим это разные формы).
теперь я хочу чтобы инициализация и конфигурация формы (!) происходила автоматом, на основании мета-данных из моего класса Employee. я хочу чтобы форма САМА поняла, что можно а что нельзя делать с этим свойством $age.
и форма и кто угодно любой другой. и поняла и получила бы адекватный ответ из класса Employee…
хочется создать такой механизм
Ага, теперь мне все стало понятно. Надо придумать, как указывать эти “мета-свойства” в модели.
Гляну, как в Ruby ActiveRecord подобное реализуется (наверняка там есть что-то похожее).
да, будет здорово услышать идеи
В моем понимании модель - это объект для работы с данными, не важно с какими данными (таблица в БД, набор таблиц, файловая система). И валидировать данные в модели, в моем понимании, не правильно. Потому что в различных местах проекта для одной и той же модели, могут быть присущи различные правила валидации.
При подходе с валидацией данных в модели Вам придется отслеживать все эти участки и учитывать при проектировании модели.
Валидация данных должна происходить в контроллере, и даже не обязательно в объекте Form (как писалось выше это просто удобная фича).
Для валидации у себя в проектах я использую цепочки валидации применяемые к моделям. Т.е. до того как данные будут сохранены.
Цепочки валидации тоже можно вынести в модели, если используются одинаковые в разных местах.
В общем смысле есть модель в ней `сохранены` данные с поста, гета. К модели применяются цепочки валидации, если все валидно, то у модели вызывается метод save который уже сохраняет данные используя DbTable, если данные не валидны, то выводится причина ошибки - все это происходит в контроллере. Кому интересно могу объяснить подробнее.
валидация данных ВНЕ места их хранения - это нарушение принципа decoupling. что в итоге приведет к разрыву связи между данными и принципами их проверки. рано или поздно мы изменим данные и забудем где находится валидация. и валидацию не изменим. в итоге получим tight coupling, что есть зло.
в общем я категорически не согласен. могу объяснить подробнее :)
Согласен с вами и так же не согласен с Dron’ом. Вы привели правильные доводы, именно поэтому, я и предложил производить валидацию непосредственно в модели.
То что в модели - тут двух мнений быть не может. А вот как потом результат этой валидации “доносить” доходчиво до всех клиентов - тут разногласия. Я считаю, что через Exceptions. Когда модель получает невалидные данные - она бросает exception. А форма должна понимать это и текст этого исключения писать под кнопкой SUBMIT. Я так вижу и так оно у меня работает. Вроде бы все логично.
У меня эксепшенами не бросается, а просто собирает список ошибок валидаторов в массив:
$user = new User;
$user->name = ”;
if (!$user->save()) { // save запускает валидацию
print_r($user->getErrorMessages());
}
Как-то так. В общем, если интересно, могу показать свою ORM :) Вообще, ищу единомышленников для создания ORM ;)
все вроде верно, только я не понял про print_r().. куда печатать то будем?
напиши мне на емейл, пообщаемся в скайпе.
print_r() - это для примера. На самом деле, я передаю объект модели в вид, и там происходит “магия” - в общем, это лучше увидеть.)
Нверное не совсем корректно описал идею, напишу немного подробнее.
Согласен с тем, что не правильно разделять хранение данных и валидацию, но хранить валидаторы в модели которая отвечает за эти данные тоже считаю неверным подходом. Считаю что валидироваться в модели должны только некорректные данные которые могут привести к ексепшену при сохранении/обновлении.
Простой пример:
Во фронтенде новый юзер при добавлении комментария, указывает ник. Валидация для ника только буквы + длина ограничена.
В баккенде при использовании этой же модели я хочу разрешить админу использовать еще и цифры пробелы и т.д. При подходе валидации данных в модели, мне придется писать костыль который скажет что надо применять другую валидацию для этого поля. А если таких различных мест много в проекте, то у нас получится очень странная модель которая пытается реализовать всю валидацию необходимую для проекта в целом.
Набором валидаторов применяемых в конкретной ситуации может выступать отдельная модель-валидатор. Которая сможет проверить данные в соответствии со своими заданными правилами валидации. Эту модель можно применять для валидации других моделей-данных. Вот как то так я себе все это представляю.
Хочу услышать пример, который приведет к нестыковке данных при таком подходе.
Спасибо за конструктивную критику.
Забыл написать что это все касается серверной валидации.