В настоящий момент Zend Framework имеет набор валидаторов, которые обычно используются для проверки входных данных из форм. Для этого используется пакет Zend_Form, отдельно же валидаторы используются достаточно редко.
Вроде бы всё хорошо, все пользуются и все довольны. Но если подумать, то в ZF валидация располагается не на том уровне, на котором хотелось бы. Я уверен, что валидация входных данных должна производиться именно в моделях и ни в коем случае не в формах, как сделано на даный момент. Почему? Остановимся на этом подробнее.
Представьте, что у вас есть форма для добавления поста в блог. Соответственно, есть класс, наследуемый от Zend_Form, который определяет, какие поля будут в форме, устанавливает на них валидаторы и, возможно, декораторы. В итоге мы получаем готовую форму, которую достаточно только вывести в нужное место шаблона - и можно использовать. Все будет валидироваться, и вроде бы все прекрасно.
Но представьте, что в один прекрасный момент вам понадобится написать робота, который должен будет опубликовывать посты, приходящие на указанный e-mail. То есть скрипт должен заходить на почтовый ящик через некоторые промежутки времени, получать по некоторым критериям письма, парсить их и опубликовывать в блоге. Получается, что форма тут не нужна - мы добавляем письма напрямую, минуя форму. А вдруг парсинг письма пройдет неправильно? Мы запросто можем получить пустой пост в блоге, или некорректный, если произойдут ошибки при парсинге.
Что делать? Чтобы избежать подобных ситуаций, придется добавлять валидатор и в модель, которая отвечает за добавление поста. Но получается, что теперь валидаторы будут указаны в двух разных местах - в форме и в модели. Это ужасно.
Чтобы избежать подобных противоречий, необходимо определять валидаторы только в конечной точке, отвечающей за непосредственное добавление поста в БД - конечно же в модели, и забыть про использование валидаторов в форме.
Кто-то скажет: так ведь форма не обязательно может добавлять данные в БД, она, например, может отправлять письмо на ту же почту, или делать что-то другое, не работая с БД. Зачем тогда определять валидаторы в модели?
Тут нужно понять, что модель - это не та штука, которая предназначена только для добавления данных в БД. Модель - это бизнес-логика, это то место, где происходит обработка поступивших данных. В Zend Framework мы имеем только один вид модели - Zend_Db_Table, и эта модель работает только с таблицами БД. Мы пока не имеем нечто среднее - модель, которая умеет работать и с таблицей и без ее использования. Когда мы получим такую функциональность, тогда мы сможем определять валидаторы в модели, будь это модель, добавляющая данные в таблицу БД, либо же не имеющая таблицы.
Я не знаю, есть ли какие-то подвижки в сторону “правильных моделей” у команды разработчиков ZF, но я очень надеюсь, что подобные желания у них есть. В любом случае, даже если это не так, я сам возьмусь за написание описанной мною функциональности.
Спасибо, надеюсь вам было интересно :)
Also interesting
Tags: zend framework, Zend_Db_Table, Zend_Form, Валидаторы, Модель
Модель в моем понимании это то что не поставляется ни в каком виде фреймворком, это логика приложения. 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() - это для примера. На самом деле, я передаю объект модели в вид, и там происходит “магия” - в общем, это лучше увидеть.)
Нверное не совсем корректно описал идею, напишу немного подробнее.
Согласен с тем, что не правильно разделять хранение данных и валидацию, но хранить валидаторы в модели которая отвечает за эти данные тоже считаю неверным подходом. Считаю что валидироваться в модели должны только некорректные данные которые могут привести к ексепшену при сохранении/обновлении.
Простой пример:
Во фронтенде новый юзер при добавлении комментария, указывает ник. Валидация для ника только буквы + длина ограничена.
В баккенде при использовании этой же модели я хочу разрешить админу использовать еще и цифры пробелы и т.д. При подходе валидации данных в модели, мне придется писать костыль который скажет что надо применять другую валидацию для этого поля. А если таких различных мест много в проекте, то у нас получится очень странная модель которая пытается реализовать всю валидацию необходимую для проекта в целом.
Набором валидаторов применяемых в конкретной ситуации может выступать отдельная модель-валидатор. Которая сможет проверить данные в соответствии со своими заданными правилами валидации. Эту модель можно применять для валидации других моделей-данных. Вот как то так я себе все это представляю.
Хочу услышать пример, который приведет к нестыковке данных при таком подходе.
Спасибо за конструктивную критику.
Простой пример:
Во фронтенде новый юзер при добавлении комментария, указывает ник. Валидация для ника только буквы + длина ограничена.
В баккенде при использовании этой же модели я хочу разрешить админу использовать еще и цифры пробелы и т.д. При подходе валидации данных в модели, мне придется писать костыль который скажет что надо применять другую валидацию для этого поля. А если таких различных мест много в проекте, то у нас получится очень странная модель которая пытается реализовать всю валидацию необходимую для проекта в целом.
Хм… на мой взгляд, тут 2 модели: “Комментатор” и “Пользователь”. Каждая модель валидируется по-своему, но хранилище могут использовать общее. Как-то так…
Совершенно правильно. Я бы тоже использовал 2 модели. Вот только задача уж слишком высосанная из пальца, как говорится - вы не должны этого хотеть)
Забыл написать что это все касается серверной валидации.
giovanni,
“валидация данных ВНЕ места их хранения - это нарушение принципа decoupling”
Тогда их нужно валидировать в базе :-) Кстати, валидация данных вне места их использования - это тоже decoupling! Так что получается, что все задачи проекта должны решаться в одной модели.
Я против того, чтобы валидировать данные в модели. Точнее я за применение “инверсии зависимостей” - т.е. вынести валидацию во внешний класс и при инстантировании модели передавать соответствующий валидатор в модель.
Теперь по существу. Перед данными модели, работающей с БД, всегда ставиться несколько вопросов:
1. Может ли она сохранить эти данные (технические ограничения);
2. Должна ли она сохранить эти данные (логические ограничения зависящие от контекста использования)
3. Может ли она использовать сохраненные данные при выполнении данной задачи.
Простой пример, задача “Подсчитать среднюю сумму одного заказа в интернет магазине” рассчет такой Sср = Sобщ / Nпок, где Sср - средняя сумма, Sобщ - общая сумма покупок, Nпок - количество покупок.
Допустим в каком-то месяце продаж не было. Это значит Sобщ и Nпок имеют нулевые значения, а это значит что ответы на вышестоящие вопросы будут следующими:
1. Может ли модель сохранить данные? - Да
2. Должна ли модель сохранить данные? - Да
3. Может ли модель использовать сохранненные данные? Нет. Делить на ноль нельзя!
Ответы на вопросы 1,2 никогда не будут контролироваться моделью, по крайней мере до тех пор пока будет использоваться БД. Я бы сказал, что модель успешно может отвечать только на вопрос 3, причем в разных случаях ответ на этот вопрос может быть разным.
Опять же не нужно забывать, что любая декомпозиция - это установка логических связей, и эти связи могут быть разными по своей силе (композиция, ассоциация). Сильная связь вполне допустима между классом модели и классом валидатора!
Ну собственно речь о fat models. Всё верно.
http://www.survivethedeepend.com/zendframeworkbook/en/1.0/the.model