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.
There is no single proper approach to your problems even within MVC pattern so simple answer to your questions does not exist.
1 Error handling
If you are using the PDO you can change error handling by setting PDO::ATTR_ERRMODE so you don't have to use exceptions but regardless of the way errors are returned it is a programmers responsibility to handle them properly.
If your method is returning false if the user doesn't exists then the controller should check what value was returned. There is no such thing as "controller assumes" it only means that the programmer assumed and didn't check returned value.
There are different errors which should be handled in different way some of them may require only showing error message some of them may be critical (like no db connection). You can use messages, views or additional controller to handle errors.
2 The proper way to show a view
Code from your example would be usually handled by the layout (header, footer, different element placement) and by the controller (using different views depending on the request). It is not good idea to use 'include' directly rather wrap it in the method. You can also create view class which would be responsible for handling views. view != .phtml
3 Managing user rights
In MVC pattern used for web applications there is usually additional "front controller" and permission checking will be handled there. You have to check for permission with each request and each request will require access to specific controller/method if you implement proper ACL it should resolve all your problems with user rights.
Best Answer
I don't know if this is what you're looking for but here goes.
Methods like save, get, getBy, getAll, etc, are generic and would ideally work across multiple models. For the most part, the only difference is going to be SPECIFICALLY where that data is. So why not make a super model of sorts for all your models to extend.
Now you can do the following without having to define getUsers: