First Answer: A key role of the model is to maintain integrity. However processing user input is a responsibility of a controller.
That is, the controller must translate user data (which most of the time is just strings) into something meaningful. This requires parsing (and may depend on such things as the locale, given that for example, there are different decimal operators etc.).
So the actual validation, as in "is the data well formed?", should be performed by the controller. However the verification, as in "does the data make sense?" should be performed within the model.
To clarify this with an example:
Assume your application allows you to add some entities, with a date (an issue with a dead-line for example). You might have an API, where dates might be represented as mere Unix time stamps, while when coming from a HTML page, it will be a set of different values or a string in the format of MM/DD/YYYY. You don't want this information in the model. You want each controller to individually try to figure out the date. However, when the date is then passed to the model, the model must maintain integrity. For example, it might make sense to not allow dates in the past, or dates, that are on holidays/sundays, etc.
Your controller contains input (processing) rules. Your model contains business rules. You want your business rules to always be enforced, no matter what happens. Assuming you had business rules in the controller, then you'd have to duplicate them, should you ever create a different controller.
Second Answer: The approach does make sense, however the method could be made more powerful. Instead of the last parameter being an array, it should be an instance of IContstraint
which is defined as:
interface IConstraint {
function test($value);//returns bool
}
And for numbers you could have something as
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);
}
}
Also I don't see what 'Age'
is meant to represent, to be honest. Is it the actual property name? Assuming there's a convention by default, the parameter could simple go to the end of the function and be optional. If not set, it would default to the to_camel_case of the DB column name.
Thus the example call would look like:
register_property('age', new NumConstraint(1, 10, 30));
The point of using interfaces is that you can add more and more constraints as you go and they can be as complicated as you want. For a string to match a regular expression. For a date to be at least 7 days ahead. And so on.
Third Answer: Every Model entity should have a method like Result checkValue(string property, mixed value)
. The controller should call it prior to setting data. The Result
should have all the information about whether the check failed, and in case it did, give reasons, so the controller can propagate those to the view accordingly.
If a wrong value is passed to the model, the model should simply respond by raising an exception.
The controller prepares data which will further be passed to the view for rendering / displaying. It also accepts user input data through a publish-subscribe mechanism or similar. Check out the first diagram on Wikipedia or Martin Fowler's website for more information about MVC.
if the view is expecting data from the controller, then they're effectively tied together as a (controller, view) pair.
While a view generally accepts data, in most MVC frameworks, it does not depend on specific controllers. Exceptions are, for instance, the JavaServer Faces family. Generally speaking, frameworks like Rails, Django or Spring MVC allow you to decouple views from controllers by passing data (the context, commonly a map/dictionary/bag) to a view (where a view is an implementation of the template view pattern).
Does the answer change when you have the concept of a strongly-typed view (like in ASP.NET MVC) or not?
Whether or not your programming language is strongly-typed has no influence on the way your are organizing your application.
Best Answer
I don't think there is a single place where you can say all the validation should go. This is because we have a few different competing programming strategies working together in a standard asp.net mvc website.
Firstly we have the idea of separating the domain logic into models, the 'action' logic into controllers and the display into a View. This is based on the idea the all the logic will take place on the server with the browser simply providing a render of the view.
Then, we extend the View by using client side javascript. This is so advanced these days that the 'one page website' idea with Jquery/knockout/angular is common practice.
This practice can be equivalent to writing a whole client side application which itself implements a MVC or MVVM pattern. We denigrate the View to a Data Transfer Object and the Controller to a service endpoint. Moving all business and UI logic into the client.
This can give a better user experience, but you are having to trust an essentially untrustworthy client. So you still need to carry out validation logic on the server, regardless of how well your client pre-validates its requests.
Also, we often have validation requirements which cant be carried out by the client. eg. 'is my new Id unique?'
Any application you create with the goal of giving the best experience/performance will necessarily borrow for multiple programming paradigms and make compromises on them to achieve its goal.