Validate on entry or before saving? - php

My brain is starting to hurt so I decided I'd ask here.
I have a data object, Employee. Getters, setters, formatters, etc. I have a manager, EmployeeManager, which handles database access and other things. Right now I have a large validation block in EmployeeManager, but I've been wondering if I could move some of that to the setters.
For example, right now I have;
public function getSSN($bFormatted = true) {
return ($bFormatted) ? $this->format_ssn($this->SSN) : $this->SSN;
}
public function setSSN($s, $bValidate = false)
{
// If we're validating user entry, save a copy.
// Either way, store a trimmed version.
if ($bValidate): $this->SSNToValidate = $s; endif;
$this->SSN = str_replace('-', '', $s);
}
public function getSSNToValidate() { return $this->SSNToValidate; }
What this does is:
* When you set an SSN, if it's being done by the system (like from the database) then it does setSSN('123456789', false), because SSNs are stored in the database sans dashes.
* When you set an SSN from user input, it does simply setSSN('123-45-6789') then not only trims the dashes, but also stores a raw version to validate (because I want to validate based on format)
* When you get an SSN, if formatting is requested (and it always is except when you're writing to the database), it formats it based on another function in the Employee class.
So my question is: Could I perhaps add the validation to the setter here, instead of relying on the monolithic validate function in the Manager class? Because I'm starting to have to deal with errors coming from all over the application, I've decided for the moment to move to a central Error Handler static class, rather than each manager maintain its own list of errors.
And because of this, I could easily add error handling to this:
public function setSSN($s, $bFromUser = false)
{
if ($bFromUser && !$this->validateSSN($s))
{
ErrorHandler::add(array('ssn' => 'Invalid SSN entered'));
}
else
{
$this->SSN = str_replace('-', '', $s);
}
}
So I guess my question is: Does this make sense at all or am I hosing myself by moving validation from the manager (to be performed on demand or just before writing to the database) to the object (to be performed on entry)?
This is overall generic, I'm just using SSN as a good example.

You should always validate when you're creating an object or setting values in the object. This way you are always guaranteed to have an object that is in a valid state. If the validation is not in the object itself you can not guarantee that the object will ever be validated.
Example validation outside of the object:
In this case there is a bug and we forgot to validate SSN.
class EmployeeManager
{
function saveEmployee($employee)
{
//Oops, we forgot to validate SSN
$db->save($employee); //This is just SQL to persist the employee.
}
}
//Somewhere else in code...
$employee = new Employee();
$employee->setSSN("FredFlintstone"); //No validation is done here.
$employeeManager = new EmployeeManager();
$employeeManager->saveEmployee($employee); //This call will persist FredFlintstone because validation was missed.
Example validation inside of the object:
In this case the employee object validates all of it's input. This way we know that if we have a instance of an employee all the data within it is valid.
class Employee
{
function setSSN($input)
{
if(strlen($input) != 9)
{
throw new Exception('Invalid SSN.');
}
//Other validations...
$this->ssn = $input;
}
}
//Somewhere else in code...
$employee = new Employee();
$employee->setSSN("FredFlintstone"); //This call will now throw an exception and prevent us from having a bad object.
$employeeManager = new EmployeeManager();
$employeeManager->saveEmployee($employee);
In addition you should never allow an object to be created that is not fully initialized. If say a SSN is required for an employee then don't supply an empty constructor. Your constructors should have parameters for all required fields (unlike my example which I omitted them for clarity).

The general rule of thumb with service-based applications is that validation should ALWAYS occur at the server end, shortly before persistence. Optionally you can perform validation on the client for a better user experience. But it's not safe to only perform validation at the client (ie, in your setter). Never rely on client data being right.
When doing client-based validation, it's preferable for each class (whether it's the model, view model, presenter, etc, depending on your architectural pattern) to validate itself rather than rely on some outside validator.

Related

Where to validate users input? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I am developing a new application using object oriented approach with some REST involved, I am not using any frameworks.
The question I have is where is the best place to validate a user’s input in a setter like below:
public function setSalary($salary)
{
if (Validator::money($salary))
$this->salary = $salary;
else
return 'Error that is an invalid number';
}
Or in the controller?
public function updateSalary()
{
$errors = array();
if (Validator::money($_POST['salary']))
$salary = $_POST['salary'];
else
$errors ['salary'] = 'Error that is an invalid number';
if(count($errors))
return $errors;
$employee = new Employee($_POST['e_Id']);
$employee->setSalary($salary);
$employee->save();
}
If I was to put in the setter how should my controller look, and return validation errors?
I have seen most people do validation in the controller, however I think should be the models responsibility for validation as it going to be using the data, and we can reuse that model without repeating ourselves.
However there can be times when validation rules may need to be different in some special cases like different validation for a different view or different validation for a supper admin.
Which one would you say is in accordance with best practices?
First of all, since you seem to aspire to implement MVC-like structure, lets start by some general mistakes, that are not related to validation directly.
Only part of your code, containing PHP superglobals, should be the bootstrap stage. Having superglobals sprinkled all over your code makes it really hard to test. And your code also becomes tightly couple to your HTML, via the <input> names.
Even if your for or if statement contains a single line, you should always use curly brackets. Well, in general your code should follow the PSR-1 and PSR-2 guidelines.
Controllers should not have any logic, or be dealing with saving of data. Read this post, maybe it clears some things up.
Ok .. now back to the original subject.
In general there are two schools of thought:
You do the validation in the domain entity
Your domain entity (in your case Employee) contains all the business roles, that pertain to it. And it can use those rules to assess, if it is in a valid state.
The code would go something like this:
$employee = new Entity\Employee;
$employee->setID($id);
$employee->setSalary($money);
if ($employee->isValid()) {
$mapper = new Mapper\Employee($dbConn);
$mapper->store($emplyee);
}
You never create invalid domain entity
This approach comes from DDD, where you domain entity is created by some other class and it can only be changes from one valid state to another valid state. Essentially, if you want to explore this approach, you will have to read this book (probably several times).
Also, there is one other validation form, which is note covered by the previous two: data integrity checks. This is type of validation, that is actually done my RDBMS. For example, the UNIQUE constraints.
When you encounter ans integrity violation, it usually would throw an exception, that you handle in service layer.
Validation must be called every time you write data to the database. So in this case from the controller. The actual validation happens in the model. The model is the object, that knows which rules it's fields obey and it can check whether the data is valid or not. Also, the model is the border between the rest of the world and the database. So, I would do something like this:
public function updateSalary()
{
$employee = new Employee($_POST['e_Id']);
$employee->setSalary($_POST['salary']));
if ($employee->validate()) {
$employee->save();
} else {
return $employee->getErrors();
}
}
Why I offer you this way:
because you keep the validation at one place. Later, if you want to validate another field, you will call the validate() method again. You won't write another validation for each field or class;
You can create a base class and put the validate() method there - all clients will call the validate() method, and wouldn't care about the specifics of the fields. The validate method will care only of what to validate - which fields and what the rules are. This information will be set in the specific (child) classes, like the Employee class.
If you want to validate only one field (like in your case), in the validate() method you can make a simple check of which fields are changed and do validation only of these fields.
Depends of you, if the validation rules are "global", in other words if they are the same every time you update that DB table/Object propriety, place them in the Model, otherwise validate user input in the Controller if in different situations you need different validation rules for the same Entity.
Firstly, I am not a geek below is just what I think.
It should be done in controller, just because right now you are only validating number, which just simple check and I think you just have to apply regex for this.
What actually I understand is that, model is where you keep your business logic, but if your field value is all together wrong than you will never process business logic and you don't want your model to come in play.
I would suggest to apply validation in the Model where possible. It has the advantage that the Model can be tested directly in a more complete way, and that the Model is guaranteed to only persist valid data.
Of course, the Controller needs to handle validation, and might be the first layer that calls on validation when it concerns complex validation on distributed items. But in the example you give there is no such complexity.
Note that anyway some validation will even be performed by the database engine (such as NOT NULL and primary key requirements).
I would also suggest to use exceptions in the Model, as this guarantees the interruption of the running function, and lets you process all (validation) errors in a similar way within the Controller. I would advise to configure your database access layer to also trigger exceptions. In case of PDO you would do that as follows:
$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
In the Model you would throw an exception when validation fails:
public function setSalary($salary) {
if (!Validator::money($salary)) {
throw new Exception('Invalid value provided as salary.');
}
$this->salary = $salary;
}
In the Controller you would catch errors and log them: as you did in $errors, but I would keep them in the Model as well for later access by the View. This illustrates how the Model detects the validation error, but the Controller deals with it.
I would also suggest to not create an Employee instance directly, but to let the Model do that for you:
public function updateSalary($emp_id, $salary) {
try {
// Note that any of the following statements could trigger exceptions:
$employee = $this->$model->getEmployee($emp_id);
$employee->setSalary($salary);
$employee->save();
} catch(Exception $e) {
$this->$model->logError('salary', $e->getMessage());
}
}
Call the latter function with the posted arguments, as this gives a better indication what the method is using as input. The top-level PHP code would look like this:
$model = new Model();
$controller = new Controller($model);
$view = new View($controller, $model);
$controller->updateSalary($_POST['e_Id'], $_POST['salary']);
echo $view->output();
The View would access to the logged errors to report them to the client.
I realise that the debate as to where to detect validation errors, where handle them, when to trigger exceptions (and when not), ... etc, will never end. But this works for me.

Doctrine setter method best practice for paired API call

I have a table that tracks a customer's status as stored in a third party database. My table should only be updated when I can successfully update the other database via an API call.
When using Doctrine, is it a bad practice to add the API call into the setter method in my entity class? For example:
public function setCustomerStatus( $cusotmerStatus )
{
$api = new externalApi();
if( $api->updateStatus( $customerStatus ) )
{
$this->customerStatus = $customerStatus;
}
else
{
return 'Could not update customer status';
}
}
If you have an Entity field that can only be set under a certain condition, you have two options; either make the check before the update:
if($api->updateStatus($customerStatus){
$entity->setCustomerStatus($customerStatus);
}
Or, make the check within the Entity, such as you have done in the set method. The advantage of containing it within the set method is that you don't leave room for error; unfamiliar developers may not know to run the check prior to calling the set method, or you just may forget. Therefore, if you can guarantee the check need always be made, I prefer the option you have chosen

Model now doing data/form validation. How to return user friendly error messages to the view?

Since today I have started validating form data in my Model layer instead of in my Controllers. I am going to shorten the code snippets as much as possible.
This is a method from my User domain object (setLastName() method is basically the same)
public function setFirstName($firstName) {
if(!$firstName) throw new \InvalidArgumentException('Some message');
if( strlen($firstName) < 2 || strlen($firstName) > 20 ) throw new \LengthException('Some message');
if(preg_match('/[^a-zA-Z\'.-\s]/', $firstName)) throw new FormatException('Some message');
$this->firstName = $firstName;
}
In my Controller I have something like this
$userService = $this->serviceFactory->build('User');
try {
$userService->register('John', 'M');
}
catch(\InvalidArgumentException $ex) {
}
catch(\LengthException $ex) {
}
catch(etc etc)
In my UserService method register() I have something like
$user->setFirstName($firstName);
$user->setLastName($lastName);
When running the setFirstName() method it will successfully set the supplied first name. The setLastName() method will throw a LengthException as it is too short.
That is what I want but when that comes back to the service layer and then to the controller and I catch it I know that a LengthException was thrown but I can't give the user a proper message like "The supplied last name was too short" because I do not know for which field the exception was thrown, just the type of exception.
How do I get around this? Thanks.
View instances should be requesting information from the model layer. Controllers is not responsible for passing the information.
This would also mean that you obsessive-compulsive use of exceptions, that cause your abstraction layers to leak, would be completely pointless. "Error" is just a state of model layer. It is an expected situation, not an exception.
Controllers in MVC are responsible for changing the state of model layer and (quite rarely) the state of current view instance. They should not be receiving any feedback from services.
Instead of returning some message, why not return a useful error message such as "supplied First name is too short". This can then be returned to the user.
Alternatively you can see that when extending exceptions you can specify additional information such as numeric codes - you could of course use this.
Or of course you can create a subclass of Exception for difference circumstances, but you could end up with hundreds of Exception subclasses which would of course be messy.
I have the same question. I think that most of the people who say that all the validation should be done in the model never developed a full PHP MVC application themselves and only know books and theory. Never a piece of code is seen about that topic.
Anyway I have thought of a possible solution. What do you think about the code below:
// Controller
$user = User::make(
$_POST['lastname'], $_POST['firstname'],
$_POST['gender'], [...]
);
if(is_array($user)) {
// store the errors in a view variable and forward, or store in session and redirect
$_SESSION['errors'] = $user;
$this->_redirect('add');
exit;
}
// Model
public static make($lastname, $firstname, $gender, [...]) {
$errors = array();
if(/* test firstname */) $errors[] = 'model_error_firstname';
if(/* test lastname */) $errors[] = 'model_error_lastname';
if(!empty($errors)) return $errors;
return new User($lastname, $firstname, $gender, [...]);
}
The model would have a static function that will return either an array with errors in case something went wrong, or a new model object if validation was ok.
In your controller you test whether an array was returned or not.
Maybe I would put the constructor of the User as private, because if you build a user with the constructor directly, you would skip all the validations. But this does not mean that it becomes a Singleton.
Maybe I would also sanitize the form fields and make them safe before passing them to the model.
The keys like model_error_xyz would be found in a translation file with the appropriate text.
Update:
Actually I think that you could just throw a custom exception from the constructor and that contains an array of messages. But the reason that I didn't propose that, is that it leads to half-constructed objects, at least in Java for example, but hey, PHP is not Java...
You would also have to validate each setter function :( it seems tedious to do validation in the model instance.
Any thoughts are welcome.

Registering a new user in the Model layer. Am I doing it right?

I have been reading up on DDD a lot over the last few days and could not find one solid example of how someone would go about simply registering a user on their site so after lots of reading I have stuck this together and I would like your feedback on it because I am sure it is far from perfect, it might even be completely wrong but here it goes:
RegisterController
$userMapper = $this->dataMapperFactory->build('user');
if($userMapper->fetchByUsername($username) !== NULL) {
// Error: The chosen username already exists
}
else {
if($userMapper->fetchByEmail($email) !== NULL) {
// Error: The email address already exists
}
else {
$userDO = $this->domainObjectFactory->build('user');
// Set the properties of the $userDO object here with the ones
// from the registration form
// Insert the new user into the database
$userMapper->save($userDO);
}
}
I have done all the form validation with my own FormValidation class so when I add the properties to the $userDO object they are all 100% ready to be inserted into the database (correct length, type, format, ranges etc) so how does the code look to you?
I think I am on the right track and I would really appreciate any tips on how to improve my code.
Also, the way I am checking if the username they chose has already been taken, is there a better way to do that? Instead of having to create an object each time to check? Like the old way I used to do it with a simple:
SELECt COUNT(*) FROM users WHERE username = 'john'
Thanks.
Some theory-related "blah":
As you might be aware, the core concept of MVC and MVC-inspired design patterns is the SoC. It dictates that you divide these patterns in to major layers: presentation layer and domain model layer.
In this case it is significant, because you current structure of controller contains application logic (the interaction domain logic entities and storage abstractions), whereas a controller should be only responsible for altering state of model layer (and sometimes - the current view) based on user input.
You end up violating bot the above mentioned SoC and also SRP.
Note: in context of web based MVC variations the "user" is a web browser, not the person sitting behind it.
Instead you should encapsulate the application logic in services (as #Gordon mentioned). In a fully realized model layer the different services become something like a public-ish API through which the presentation layer interacts with model.
Though, unlink Gordon, I would recommend your service to be a bit broader. In case of user registration, I would make it a part of CommunityService or maybe MembershipService. A structure that handles all the aspects of the user account management as far as the model layer is concerned.
The code bits:
One way for using in controller would look something like:
public function postUser( $request )
{
$community = $this->serviceFactory->build('Community');
$community->addUser( $request->getParameter('username'),
$request->getParameter('password'),
$request->getParameter('repeated_password'),
$request->getParameter('email') );
}
While this is a valid way, you might already notice an possible problem. Even when user registration need only the minimum of data, the amount of parameters that you end up passing to the service makes it hard to use.
Passing the $request on to service is not a valid improvement. You would just end up violating Law of Demeter. Instead i would recommend something like:
$keys = ['username', 'password', 'repeated_password', 'email'];
$community->addUser( $request->getParameters( $keys ) );
Where the getParameters() method is implemented similar to:
public function getParameters( $keys )
{
$response = [];
foreach( $keys as $parameter )
{
$response[ $parameter ] = $this->getParameter( $parameter );
}
return $response;
}
Domain logic and validation
You mentioned, that some FormValidation class, that you are using to make sure, that your instance of User domain object receives proper values. Actually the data validation is one of the domain object's responsibilities. You still might use a separate validation class, to avoid code duplication, but that would be a dependency, which is injected by domain object's factory to share between instances.
Note: in my personal experience, the duplication for validation is quite rare for anything but the null-checks. Each of complicated validation rule-sets are targeted at fields of one specific domain object. That, in my opinion, makes a validation class quite redundant ... unless you expect to share same validation class between multiple projects.
The code-flow usually is such that, when you need to store the data from domain object, you check if it has not acquired an error state, and if there is an error, you actually dump it in session, for a retrieval after redirect.
if ( $user->isValid() )
{
$sqlMapper->store( $user );
}
else
{
$sessionMapper->storeUser();
}
In this use-case pre-validated input ends up actually being harmful.

Validate a domain entity upon setting each property, or all at once later?

When validating a domain entity, is it better to validate the values as they are set, or all at once with a validator (such as in Symfony2) later on?
For example:
Option 1. Validate while being set
public function setEmail($email)
{
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new EntityException('The specified email address ' . $email . ' is invalid.');
}
$this->_email = $email;
return $this;
}
Option 2. Validate later...
$user = new UserEntity();
$user->setEmail('johnnie#duh.com');
$validator = new Validator();
$validator->validate($user);
Option 3. Both of the above (although seems a little redundant and not worth the overhead).
I know that the first one probably makes for more airtight entities, but the second one makes for more user friendly error handling.
Also, the second option seems easier to configure and maintain...as I don't have to adjust the setters, and I can centralize my validation logic to a single class.
What it boils down to...
So basically, it sounds like Option 2 is what I WANT to do, but feel like sacrificing the airtight-ness of the entity (for example, if I forget to run the entity through the validator), might be stupid.
There are doubtless going to be situations where validation is unnecessary at the application level (i.e., you know you are working with valid data at a particular point). Although there's nothing wrong with applying validation in these cases, it's certainly superfluous.
In addition, you might find yourself in a scenario where you want to perform a "mass validation" of incoming data. This would likely apply more to a service or to custom-implemented forms where you don't have e.g., Symfony's form validation framework to fall back on.
These would tend to indicate that Option 2 is the more desirable way to go, perhaps with caveat that the validator should be invoked automatically when attempting to persist the object (e.g., during the model's pre-save event; I'm not familiar with Doctrine 2 yet, but Doctrine 1.2 does have its own validation framework that can accomplish this).
Having invalid data is surely never a desirable scenario, but so long as you can be assured that invalid data are not persisted, this gives you the freedom to trust that any data you pull from the database are valid (note that I say "valid" here, not "trustworthy"!).
Single Responsibility Principle
The best is to have all necessary validations in a separated layer. With that will be easier to maintain and test the validators. Also easier to validate data across your application.
Don't Repeat Yourself
You don't have to call validate() for each entity.
All you have to do is to implement the validation on your repository layer or service layer if you have one.
$user = new User();
// [...]
$user->setEmail('myinvalidemail#blah,com');
$repository->save($user);
So in your user's repository
UserRepository extends AbstractRepository {}
And the common validation for all entities:
abstract class AbstractRepository {
public function save($entity) {
$validator = // Get your validator based on the entity's name or something else
$validator->validate($entity); // Throws exceptions or flag the fields for future use
// Now save it...
}
}

Categories