Первый ответ: ключевая роль модели заключается в поддержании целостности. Однако обработка пользовательского ввода является обязанностью контроллера.
То есть контроллер должен переводить пользовательские данные (которые в большинстве случаев являются просто строками) во что-то значимое. Это требует анализа (и может зависеть от таких вещей, как локаль, учитывая, что, например, существуют разные десятичные операторы и т. Д.).
Таким образом, фактическая проверка, как в «правильно ли сформированы данные?», Должна выполняться контроллером. Однако проверка, как в «данные имеют смысл?» должны быть выполнены в рамках модели.
Чтобы прояснить это на примере:
Предположим, что ваше приложение позволяет вам добавлять некоторые сущности с датой (например, проблема с тупиком). У вас может быть API, где даты могут быть представлены в виде простых меток времени Unix, а при переходе с HTML-страницы это будет набор различных значений или строка в формате MM / DD / YYYY. Вы не хотите эту информацию в модели. Вы хотите, чтобы каждый контроллер по отдельности пытался выяснить дату. Однако когда дата затем передается модели, модель должна сохранять целостность. Например, может иметь смысл не разрешать даты в прошлом или даты, которые относятся к праздникам / воскресеньям и т. Д.
Ваш контроллер содержит правила ввода (обработки). Ваша модель содержит бизнес-правила. Вы хотите, чтобы ваши бизнес-правила всегда соблюдались, независимо от того, что происходит. Предполагая, что у вас есть бизнес-правила в контроллере, вам придется их дублировать, если вы когда-нибудь создадите другой контроллер.
Второй ответ: подход имеет смысл, однако этот метод можно сделать более мощным. Вместо того, чтобы последний параметр был массивом, он должен быть экземпляром, IContstraint
который определяется как:
interface IConstraint {
function test($value);//returns bool
}
И для чисел вы могли бы иметь что-то
class NumConstraint {
var $grain;
var $min;
var $max;
function __construct($grain = 1, $min = NULL, $max = NULL) {
if ($min === NULL) $min = INT_MIN;
if ($max === NULL) $max = INT_MAX;
$this->min = $min;
$this->max = $max;
$this->grain = $grain;
}
function test($value) {
return ($value % $this->grain == 0 && $value >= $min && $value <= $max);
}
}
Также я не понимаю, что 'Age'
значит представлять, если честно. Это фактическое имя свойства? Предполагая, что по умолчанию существует соглашение, параметр может просто идти до конца функции и быть необязательным. Если не задано, по умолчанию используется значение to_camel_case имени столбца БД.
Таким образом, пример вызова будет выглядеть так:
register_property('age', new NumConstraint(1, 10, 30));
Смысл использования интерфейсов в том, что вы можете добавлять все больше и больше ограничений по мере продвижения, и они могут быть настолько сложными, насколько вы хотите. Для строки, соответствующей регулярному выражению. На свидание должно быть не менее 7 дней. И так далее.
Третий ответ: у каждого объекта Model должен быть такой метод Result checkValue(string property, mixed value)
. Контроллер должен вызвать его до установки данных. Он Result
должен располагать всей информацией о том, провалилась ли проверка, и, если она прошла, указать причины, чтобы контроллер мог соответствующим образом передать их в представление.
Если в модель передается неправильное значение, модель должна просто ответить, вызвав исключение.
Я не полностью согласен с «back2dos»: я рекомендую всегда использовать отдельный слой формы / валидации, который контроллер может использовать для проверки входных данных перед их отправкой в модель.
С теоретической точки зрения валидация модели работает с доверенными данными (внутренним состоянием системы) и в идеале должна повторяться в любой момент времени, тогда как валидация ввода явно выполняется один раз для данных, поступающих из ненадежных источников (в зависимости от варианта использования и привилегий пользователя).
Такое разделение позволяет создавать многократно используемые модели, контроллеры и формы, которые могут быть свободно связаны посредством внедрения зависимостей. Думайте о проверке входных данных как о проверке белого списка («принять заведомо хорошее»), а о проверке модели как о проверке черного списка («отклоните заведомо плохое»). Проверка белого списка более безопасна, в то время как проверка черного списка предотвращает чрезмерное ограничение уровня вашей модели для очень специфических вариантов использования.
Неверные данные модели всегда должны вызывать исключение (в противном случае приложение может продолжить работу, не замечая ошибки), в то время как неверные входные значения, поступающие из внешних источников, не являются неожиданными, а скорее распространены (если только у вас нет пользователей, которые никогда не делают ошибок).
Смотрите также: https://lastzero.net/2015/11/why-im-using-a-separate-layer-for-input-data-validation/
источник
Да, модель должна выполнить проверку. Пользовательский интерфейс также должен подтвердить ввод.
Очевидно, что ответственность за определение действительных значений и состояний лежит на модели. Иногда такие правила часто меняются. В этом случае я бы кормил модель из метаданных и / или украшал ее.
источник
Отличный вопрос!
Что касается разработки всемирной паутины, то что, если вы спросите следующее, также.
«Если неправильный пользовательский ввод поступает в контроллер из пользовательского интерфейса, должен ли контроллер обновлять представление в виде циклического цикла, заставляя команды и входные данные быть точными перед их обработкой ? Как? Как представление обновляется при нормальном режиме? Условия? Является ли представление тесно связано с моделью? Является ли проверка ввода пользователя основной бизнес-логикой модели или она является предварительной по отношению к ней и, следовательно, должна происходить внутри контроллера (поскольку пользовательские входные данные являются частью запроса)?
(В сущности, можно и нужно ли откладывать создание экземпляра модели до тех пор, пока не будет получен хороший ввод?)
Мое мнение таково, что модели должны управлять чистыми и нетронутыми обстоятельствами (насколько это возможно), не обремененными базовой проверкой входных данных HTTP-запроса, которая должна происходить до создания экземпляра модели (и, безусловно, до того, как модель получит входные данные). Поскольку управление данными состояния (постоянными или иными) и связями API - это мир модели, пусть базовая проверка ввода HTTP-запроса происходит в контроллере.
Подводя итоги.
1) Проверьте ваш маршрут (проанализированный по URL), так как контроллер и метод должны существовать, прежде чем что-либо еще может продолжаться. Это определенно должно произойти в области фронт-контроллера (класс маршрутизатора), прежде чем попасть к настоящему контроллеру. Duh. :-)
2) Модель может иметь много источников входных данных: HTTP-запрос, база данных, файл, API и, да, сеть. Если вы собираетесь поместить все свои входные проверки в модель, то вы считаете, проверку ввода HTTP-запроса часть бизнес-требований для программы. Дело закрыто.
3) Тем не менее, это близоруко, чтобы пройти через создание множества объектов, если ввод HTTP-запроса бесполезен! Вы можете знать, хорошо ли ** ввод HTTP-запроса ** ( который пришел с запросом ), проверив его перед созданием экземпляра модели и всех ее сложностей (да, возможно, еще больше валидаторов для данных ввода-вывода API и БД).
Проверьте следующее:
а) Метод HTTP-запроса (GET, POST, PUT, PATCH, DELETE ...)
б) Минимальный контроль HTML (вам достаточно?).
в) Максимальный контроль HTML (у вас слишком много?).
г) Правильные HTML-элементы управления (у вас есть правильные?).
e) Входное кодирование (обычно это кодировка UTF-8?).
f) Максимальный размер ввода (какой-либо из входных данных выходит за границы?).
Помните, что вы можете получать строки и файлы, поэтому ожидание создания экземпляра модели может оказаться очень дорогим, так как запросы попадают на ваш сервер.
То, что я описал здесь, попадает в цель запроса, поступающего через контроллер. Пропуск проверки намерения делает ваше приложение более уязвимым. Намерение может быть только хорошим (игра по вашим основным правилам) или плохим (выход за пределы ваших основных правил).
Намерение для запроса HTTP является предложением «все или ничего». Все проходит, или запрос недействителен . Не нужно ничего отправлять на модель.
Этот базовый уровень намерения HTTP-запроса не имеет ничего общего с обычными ошибками ввода пользователя и проверки. В моих приложениях HTTP-запрос должен быть действителен пятью способами, указанными выше, чтобы я мог выполнить его. В оборонном углубленной манере говорить, вы никогда не получите проверки входных данных пользователя на стороне сервера , если любые эти пяти вещей , которые терпят неудачу.
Да, это означает, что даже ввод файла должен соответствовать вашим попыткам внешнего интерфейса проверить и сообщить пользователю максимальный допустимый размер файла. Только HTML? Нет JavaScript? Хорошо, но пользователь должен быть проинформирован о последствиях загрузки файлов, которые слишком велики (главным образом, они потеряют все данные формы и будут выгнаны из системы).
4) Означает ли это, что входные данные HTTP-запроса не являются частью бизнес-логики приложения? Нет, это просто означает, что компьютеры являются конечными устройствами, а ресурсы должны использоваться с умом. Имеет смысл прекратить вредоносную деятельность раньше, а не позже. Вы платите больше вычислительных ресурсов за ожидание, чтобы остановить его позже.
5) Если ввод HTTP-запроса плохой, весь запрос плохой . Вот как я на это смотрю. Определение правильного ввода HTTP-запроса получено из бизнес-требований модели, но должна быть некоторая точка разграничения ресурсов. Как долго вы оставите плохой запрос в живых, прежде чем убить его и сказать: «Эй, неважно. Плохой запрос».
Суждение заключается не просто в том, что пользователь допустил разумную ошибку ввода, а в том, что HTTP-запрос настолько запредельный, что он должен быть объявлен вредоносным и немедленно прекращен.
6) Таким образом, за мои деньги HTTP-запрос (МЕТОД, URL / маршрут и данные) либо ВСЕ хорош, либо НИЧЕГО не может продолжаться. У надежной модели уже есть задачи проверки, о которых нужно заботиться, но хороший пастор ресурсов говорит: «Мой путь или верный путь. Приходите правильно или не приходите вообще».
Это ваша программа, хотя. «Существует более одного способа сделать это». Некоторые способы стоят больше времени и денег, чем другие. Проверка данных HTTP-запроса позже (в модели) должна стоить дороже в течение всего жизненного цикла приложения (особенно в случае увеличения или уменьшения).
Если ваши валидаторы являются модульными, проверка правильности ввода * HTTP-запроса ** в контроллере не должна быть проблемой. Просто используйте стратегический класс Validator, в котором валидаторы иногда также состоят из специализированных валидаторов (электронная почта, телефон, токен формы, капча, ...).
Некоторые считают, что это совершенно неправильно, но HTTP был в зачаточном состоянии, когда «Банда четырех» написала « Шаблоны проектирования: элементы многократно используемого объектно-ориентированного программного обеспечения» .
================================================== ========================
Теперь, поскольку это относится к обычной проверке пользовательского ввода (после того, как HTTP-запрос был признан действительным), он обновляет представление, когда пользователь ошибается, о чем вам нужно подумать! Этот вид проверки ввода пользователя должен происходить в модели.
У вас нет гарантии JavaScript на фронтэнде. Это означает, что у вас нет возможности гарантировать асинхронное обновление пользовательского интерфейса вашего приложения со статусами ошибок. Истинное прогрессивное улучшение будет также охватывать случай синхронного использования.
Учет синхронного варианта использования - это искусство, которое теряется все больше и больше, потому что некоторые люди не хотят тратить время и хлопоты на отслеживание состояния всех своих приемов пользовательского интерфейса (показать / скрыть элементы управления, отключить / включить элементы управления , сообщения об ошибках, сообщения об ошибках) на стороне сервера (обычно по состоянию отслеживания в массивах).
Обновление : на диаграмме я говорю, что
View
ссылка должнаModel
. Нет. Вы должны передать данныеView
от,Model
чтобы сохранить слабую связь.источник