I have a program where users can log in. I have a User
class and when they log in, all of their attributes (permissions, username, real name, etc) are saved as class properties. However I am having difficulty wrapping my mind around what the best way is to have the user update their own info due to the fact that all data needs to be validated.
Currently, when a user tries to update their own information, e.g. their email address, I do:
// Set new email
$try = $user->setEmail($_POST['new_email']);
if ($try !== true) {
$errors[] = $try;
}
...
if (isset($errors) {
// Tell user what went wrong
} else {
$user->saveValuesToDB();
}
It works fine, but seems rather ugly to me since User->setEmail()
needs to have a mixed return type because the email can be invalid for a multiple number of reasons (domain not found, invalid format, empty string provided, already used by another user, etc) and the exact reason needs to be passed back to the user, but as I understand mixed returns are generally discouraged.
Another way I thought of doing it is just updating all of the properties without performing any sort of validation and then doing User->commitAllChanges()
at the end which would perform all of the validations, which would take the multiple returns out of all the setter method and reduce it down to only the commit method, but I really don't like this either because I feel that any properties actually being set should be valid and it still doesn't completely get rid of the problem.
What are some other method(s) that I can use to allow the user to set object properties and also validate them, sending any error back to the user? Or is what I'm doing now fine?
One way is to keep your solution (setEmail function) and make it more OOP. Inside the setAction you can sett a list of errors
Then make 1 or 2 functions to get erros from the class
This is generally a good approach. You can also break down the whole process into smaller pieces so that it's more versatile.
For example, the model might have a
validate
method that returns a single boolean (is everything OK, or is there one or more errors?), and agetValidationErrors
method that returns an array which holds all the information you care about (would probably need to be multidimensional). It could have acommit
method that automatically callsvalidate
and only persists the changes if there are no errors at all.Properties "that just won't be set" are IMO way more trouble than one might initially think. The major problem is that if a property objects to being set (either by returning some error code or, much better, by throwing an exception) then you have to wrap all access to it with "protective" code. This gets very tedious very fast.
In practice, it's much more convenient to massively assign values to all of the properties in one go through a helper method and then see what the result was (e.g. by calling a
validate
method as above).Also, do not forget that in practice you 'd want to display validation errors next to the data input fields, and those fields are wired to be pre-populated with the values of your model's properties. So on the one hand the properties need to be valid, and on the other hand they need to match exactly what the user entered (otherwise the users will want to kill you). It's therefore better to relax the "must always be valid" constraint in favor of utility -- although these days with AJAX forms and client-side validation everywhere, this is becoming less relevant.
Use a
try-catch
construction.I use something of a MVC.NET approach on this. Basically the bad in the throwing an exception is performance and why would you only want to catch one? If both email and password are invalid just let them know at the same time rather then 'email is incorrect' - fix email - repost - 'oh password was also incorrect probably could have told you sooner but hey you only make me money'
Nothing wrong with your current method, but what you could do is use exceptions.
It might look like:
And your setEmail method would be something like: