Where object should be validated? - php

My problem is that I don't know, witch solution for the validation of input is better and if there is another, better solution. Well, where object should be validated? On the one hand, the object should always be correct. On the other hand, if the user specifies several incorrect data, it's a more elegant solution to notify him of all errors, not just one (first occured).
// Solution 1:
try {
$user = new User();
$user->setFirstname($_POST['firstname']);
$user->setSecondname($_POST['secondname']);
$user->setLastname($_POST['lastname']);
$user->hasLeftHand($_POST['has-left-hand']);
$user->hasRightHand($_POST['has-right-hand']);
$user->setHandedness($_POST['handedness']);
$user->save($pdo);
} catch (Exception $e) {
echo $e->getMessage();
}
// Solution 2:
$user = new User();
$user->setFirstname($_POST['firstname']);
$user->setSecondname($_POST['secondname']);
$user->setLastname($_POST['lastname']);
$user->hasLeftHand($_POST['has-left-hand']);
$user->hasRightHand($_POST['has-right-hand']);
$user->setHandedness($_POST['handedness']);
$errors = $user->validate();
if (empty($errors)) {
$user->save($pdo);
} else {
echo 'Some errors occured: ' . implode(', ', $errors);
}
// Solution 3:
try {
$user = new User();
$user->setFirstname($_POST['firstname']);
$user->setSecondname($_POST['secondname']);
$user->setLastname($_POST['lastname']);
$user->hasLeftHand($_POST['has-left-hand']);
$user->hasRightHand($_POST['has-right-hand']);
$user->setHandedness($_POST['handedness']);
$user->save($pdo);
} catch (Exception $e) {
$errors = $user->validate();
echo 'Some errors occured: ' . implode(', ', $errors);
}
In solution 1 each set method validates the input. Therefore, the object is always correct. The save method only saves the object in the database. On the other hand, if all the data is incorrect, it will be displayed only the first error.
In solution 2 we allow that object can not be correct between set calls, but to the database can be saved only valid object. set methods do not validate the input. validate method validates the object as a whole, and returns a list of all errors found. save method looks like this:
public function save(PDO $pdo)
{
if(! empty($this->validate())) {
throw new Exception('Invalid state');
}
// Store in database
}
In this solution is easier to validate the object. Becouse, how should work code below in solution 1?
$user->hasLeftHand(true);
$user->hasRightHand(false);
$user->setHandedness('right');
Or this code:
$user->setHandedness('right');
$user->hasLeftHand(true);
$user->hasRightHand(false);
Solution 3 is a copy of solution 2. Code of User class is the same. Changes only its use - try-catch block. In my opinion, this code looks more clearly.

Validation of input should be separated from validating the correctness of a domain object itself. Many frameworks use Form classes for this. Maybe have a look at how it's done in:
Symfony
Zend Framework (Form InputFilters)
In short, the form will validate the input, and will populate a bound object in case the data is valid.
disclaimer: this question is opinion based, and there is no 1 correct answer... but I'll true to recap what we discussed in comments and how I think most people solve it.
separate input validation from the model
<?php
// class for input validation
class UserValidator
{
public function validate(array $data)
{
$errors = array();
if (isset($data['email'])) {
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'email not valid';
}
} else {
$errors['email'] = 'email is required';
}
}
}
// model class itself, does not implement extensive validation
class User
{
protected $email;
public function setEmail($email)
{
// only make sure we have a valid string, dont validate email again
if (!is_string($email) || !strlen($email)) throw new \InvalidArgumentException('invalid email given');
$this->email = $email;
return $this;
}
}
So this example separates the input validation so it is easy to provide user feedback when processing form data. The model itself only performs basic checks, and assumes the developer is smart enough to set sane data...
<?php
// where you process a form POST...
$validator = new UserValidator();
$errors = $validator->validate($_POST);
if (count($errors)) {
// provide feedback to your user, he gave us bogus data...
return $errors;
}
// if we are here, we passed validation and can assume our data is good
$user = new User();
$user->setEmail($_POST['email']);
This is a very simplified example, again you should check out how the major frameworks solve this problem, they've had many people thinking hard about this already...
And as the differences between Zend and Symfony underline: there is no golden hammer for this.

Related

How to avoid PHP nested if...else... conditions and write clean, easy to understand code?

I am writing a login method in codeigniter where user will enter email and password and then it will do following things:
Validate user input -> If valid do point#2
Get user details from database -> If success query do point#3
Verify the password -> If verified login to the system
I know I can achieve the above by using pure nested if...else condition but then it will be very tedious and hard to read.
So, I tried it in this way -
// Empty array to hold processing errors
$processing_errors = array();
// Check for valid input
if(!$is_valid_input) array_push($processing_errors, "Invalid inputs");
// Get user details
if($get_user_details)
{
// Verify the user
if(!$is_correct_user) array_push($processing_errors, "Incorrect username or password.");
}
else array_push($processing_errors, "Database query error");
// Check if processing errors is empty
if(empty($processing_errors)) echo "Successfully logged in."; // Success
else foreach($processing_errors as $errors) echo $errors; // Errors
But the problem with above approach is it will still execute the code after Check for valid input even if it returns false and will take unnecessary processing time.
What is the best approach to write neat and clean readable code?
You can break these into methods exposing only the login request method and keeping everything else internal(as in private methods) as shown below. userLogin is where the request comes in.
Snippet:
class LoginController extends CI_Controller{
private $errors;
function __construct(){
$this->errors = []; // initialize error bag to an empty array
/*
load needed models
*/
}
private function validateInput($post_data){
/*
validate user details
*/
if(!$is_valid_input){
$this->errors[] = '';
}
return $is_valid_input;
}
private function getUserDetails($post_data){
/*
get the user data using models
*/
return $user_data;
}
private function verifyUser($post_data){
$user_data = $this->getUserDetails($post_data);
$is_correct_user = $user->email == $post_data['email'] && password_verify($post_data['password'],$user->password);
if(!$is_correct_user){
$this->errors[] = 'Incorrect username or password.';
}
return $is_correct_user;
}
private function displayErrors(){
foreach($this->errors as $current_error){
echo $current_error,"<br/>";
}
}
public function userLogin(){
$data = $this->input->post();
try{
if($this->validateInput($data) && $this->verifyUser($data)){
echo "Successfully logged in";
/* Your futher code */
}else{
$this->displayErrors();
}
}catch(Exception $e){
echo $e->getMessage(); // if you throw some Exception in your model for some situations. Better to have exact class name of Exception for faster lookup of the class
}
}
}

How do I add my message to the $errors variable in Laravel 4.2

I tried extending the Laravel 4.2 validation to check if all form data is valid (not just some fields) according to some business rules.
I could not extend the custom validator in any normal way (either too hacky or not DRY enough) so I decided to add the business rules check after validator succeeds, and then add my own error messages and redirect.
It is still ugly and it does not work but for now I would be happy if someone tells me how to add my message to the session. If you feel like telling me how I should use Laravel's custom validators I've included some more code so you can be more specific..:
$validator = Validator::make(Input::all(), $this->validationRules, $this->validationMessages);
if ($validator->fails()) {
if (0 == $id) {
return Redirect::route('x.create')->withErrors($validator)->withInput();
} else {
return Redirect::route('x.edit', $id)->withErrors($validator)->withInput();
}
} else {
//TESTs
if ($this->AlreadyExists($id)) {
$messages = new MessageBag();
if ($request->getSession()->get('errors')){
$messages = $request->getSession()->get('errors')->getBag('default');
}
$messages->add('form', 'same data already exists');
if (0 == $id) {
return Redirect::route('x.create')->withErrors($messages)->withInput();
} else {
return Redirect::route('x.edit', $id)->withErrors($messages)->withInput();
}
}
}
//all is ok. Save/update entity
...
The code for setting the session is first 5 lines after check for AlreadyExists. I got it from some forum but it doesn't seem to work ok (I get some "non-object" exceptions if I include the code so it seems it corrupts the session object)
I don't think I have time to upgrade to L5.
I solved this by creating a new error message bag and adding it to redirect:
$mb = new Illuminate\Support\MessageBag();
$mb->add("form", "same data already exists");
...
return Redirect::route('x.create')->withErrors($mb)->withInput();
Seems I needed to add full namespace for bag, but most of all I needed rest :)
The validation logic is still bad but no time to tinker with that now.
for anyone who uses form request I suggest to take a look at this implementations. formatErrors in laravel 5.1 documentations mentioned for a way to manipulate the way errors display. I just simply jump in it and added some codes to extend errors.
use Illuminate\Contracts\Validation\Validator;
class ProfileChangePasswordRequest extends Request
{
public function authorize()
{
if (auth()->check())
return true;
return false;
}
public function rules()
{
return [
'password' => "required|confirmed|min:6"
];
}
protected function formatErrors(Validator $validator)
{
if ($this->isCurrentPasswordValidate()) {
return $validator->errors()->all();
} else {
$validator->errors()->add('current_password', 'current password is wrong');
return parent::formatErrors($validator);
}
}
public function isCurrentPasswordValidate()
{
return auth()->validate(['email' => auth()->user()->email,
'password' => $this->current_password]);
}
}

MVC Validation Advice

I'm currently validating input and returning errors in a "fat controller" as follows:
class SomeController
{
public function register()
{
// validate input
$username = isset($_POST['username']) && strlen($_POST['username']) <= 20 ? $_POST['username'] : null;
// proceed if validation passed
if (isset($username)) {
$user = $this->model->build('user');
if ($user->insert($username)) {
$_SESSION['success'] = 'User created!';
} else {
$_SESSION['error'] = 'Could not register user.';
}
} else {
$_SESSION['failed']['username'] = 'Your username cannot be greater than 20 characters.';
}
// load appropriate view here
}
}
class SomeModel
{
public function insert($username)
{
// sql for insertion
// ...
return $result;
}
}
While this works and is easy enough for me to implement, I understand that this is incorrect because the validation belongs in the model, which I'm attempting to correct using a "fat model" as follows:
class SomeController
{
public function register()
{
$user = $this->model->build('user');
$user->insert($_POST['username']);
// load appropriate view here
// ...
}
}
class SomeModel
{
public function insert($username)
{
// validate input
$error = false;
$username = trim($username) != '' && strlen($username) <= 20 ? $username : null;
// proceed if validation passed
if (isset($username)) {
// sql for insertion
// ...
$_SESSION['success'] = 'User created!';
} else {
// store error in session
$error = true;
$_SESSION['error']['username'] = 'Your username cannot be greater than 20 characters ';
}
return $error ? false : true;
}
}
The problem I see here is that the model is supposed to be portable, in that it should never need to change. But if the requirement for the length of $username changes, then obviously I'll have to alter my model.
I feel like this may be a really common question but I've yet to find a straight-forward answer. Without implementing any extra "layers", "mappers" or whatever other confusing terms are out there, how could the example pseudo-code provided be modified to correctly handle this transaction? (eg, validate input, return error if validation fails)?
Without implementing any extra "layers", "mappers" or whatever
You should consider the "model" to be a application layer rather than a single class. The term "layer" could be thought of as a simple way to reference the M slice of MVC sandwich. So to accomplish the flexibility you desire you will need to create it.
A number of clear seperations can be made. I would consider having three abstractions: services, data mappers and entities.
A service would be exposed to the controller and perform the service being requested.
// some controller
function register() {
$service = $this->getUserService();
$user = $service->register($_POST['first_name'], $_POST['last_name']);
if ($user instanceof \My\Entity\User) {
// set user in view
} else {
// redirect to error
}
}
So task one complete, the controller is now dumb to whatever happens within register, all it wants to know is how to resolve the appropriate result. If there is a user object, success, otherwise false something went wrong.
The service class itself would encapsulate the services being offered:
// class UserService.php
function register($firstname, $lastname) {
// validate arguments
if ($this->isValidUsername(....
$userMapper = $this->getUserMapper();
$user = new My\Entity\User();
$user->setFirstName($firstname);
$user->setLastName($lastname);
return $userMapper->save($user);
}
return false;
}
We handle the validation of the arguments and also create the new user, passing it to the data mapper which will perform the "actual save" abstracting the database operations.
// UserMapper
function save($user) {
// save $user to db
$sql = 'INSERT INTO ....
return true;
}
I'm not sure what you would consider to be an undesirable "layer" or "mapper". This is an interesting question, and my first though was that you could just include a configuration file that defined a constant for your username length. My second though was that you could have someModel extend a class or implement an interface, wherein you values would be set as properties or constants. I suspect that you have thought of these, and are avoiding them; that this is what you mean by avoiding "layers" and "mappers" It seems that you are being guided by these principals in this code:
Avoid "magic numbers"
KISS
Composition over inheritance
skinny controller/fat model
So, are you running php5.4+ ? Maybe define a trait which could be used in this and other models that defines the username length and other changeable values in the application. Or maybe that too is to much of a "layer"?

Structure of a form validation class

I want to make a class in OOP PHP to validate forms. However, I've having trouble structuring this.
Initially I thought of creating individual functions for each type of validation (check length of submitted data, check whether it's a number or not, etc), then another function to check whether data passed the validation tests and pass errors into an array.
I'm getting stuck though as my code is becoming very long and difficult to manage- I'm pretty new, so how would you approach this problem?
As i was reading through your post, a question came into my mind about what you write:
Why, instead of validating a form, dont you validte your model's objects?
I mean, in an OOP way of looking things your model´s object (or domain objects) are the ones who knows what data is valid or not for each of their attributes.
Not doint that, and pushing that logic into the UI makes your design fragile, UI dependant and harder to maintain. If you add a new attribute to one of your model's object, you'll have to modify the form validator as well.
If you go with Objects Validation, the idea is that an object cannot be created in an invalid state. If you try to modify it with invalid data, an exception will be thrown.
This makes easy to work with forms. The only think you have to do is populate your objects and watch for exceptions thrown in that process.
This is only an idea to get you started and see another way of solving this problem.
Regarding your question about Forms Validation, as the other guys said, it is always better not to reinvent the wheel and go for an existing, proven, validation framework.
However, if you are curious about it, here is one of the many ways you can do it:
Let's go through the things you need: you are talking about a form that needs to be validated with one or more validation functions. Then you talk about a function that tells you whether the form passed the validation or not, and as a result you got the list of errors found during the validation phase.
As you talk about OOP, the way to go is to give each concept or idea of your problem domain (the domain of form validation) entity via a class that represents it that model the behavior they have.
So, it is natural to think about a FormValidator class with a list of ValidationRule instances, where each one colaborates in the validation process. This validation process is done by calling the validate function of the FormValidator. Also, each ValidationRule will give, as result of calling it´s own validate method an instance of the ValidationRuleResult class, that tells whether the validation was successful or not, along with an error message and additional data (if needed) about the validation. Once all the validation rules were evaluated, the validate method of the FormValidator class will return an instance of ValidationResult class, that summarizes all the validation results of the rules evaluated providing the list of errors found.
To get this down to earth, here is the sample model we're talking about:
A sample implementation
Disclaimer: please bear in mind that, as any design, it may contains flaws. The following is intended to help you to solve your problem, not to be a complete solution.
class FormValidator {
private $_validationRules;
function __construct() {
$this->_validationRules = array();
}
// Registers a new validation rule
function addRule($aValidationRule) { $this->validationRules[] = $aValidationRule; }
// Validates $aForm, evaluating each of the $_validationRules defined
function validate($aForm) {
$aValidationResult = new ValidationResult();
foreach($this->_validationRules as $aValidationRule) {
$aValidationRuleResult = $aValidationRule->validate($aForm);
$aValidationResult->addResult($aValidationRuleResult);
}
return $aValidationResult;
}
}
abstract class ValidationRule {
private $_fieldName;
// The form's field name to be validated
function __construct($aFieldName) {
$this->_fieldName = $aFieldName;
}
function fieldName() { return $this->_fieldName; }
// Returns an instance of ValidationResult describing the result of evaluating the ValidationRule in $aForm.
abstract public function validate($aForm);
}
class ValidationResult {
private $_validationRuleResults;
function __construct() {
$this->_validationRuleResults = array();
}
// Registers a validation rule result
function addResult($aValidationRuleResult) {
$this->_validationRuleResults[] = $aValidationRuleResult;
}
// Returns the list of the error messages of the validation rule results that did't passed
function errorsFound() {
$errors = array();
foreach($this->validationRuleResults as $aValidationResult) {
if ($aValidationResult->passed()) continue;
$errors[] = $aValidationResult->errorMessage();
}
return $errors;
}
// Tells whether all the validation rule results passed or not
function validationPassed() {
foreach($this->validationRuleResults as $validationResult) {
if ($validationResult->passed() == false) return false;
}
return true;
}
}
class ValidationRuleResult {
private $_passed, $_error_message;
function __construct($passed) {
$this->_passed = $passed;
$this->_error_message = '';
}
// Tells whether the form passed this validation rule or not
public function passed() { return $this->_passed; }
public function
// The error message should be empty if passed to avoid confusion
public function errorMessage { return $this->passed() ? '' : $this->_error_message; }
public function setErrorMessage($anErrorMessage) { $this->_error_message = $anErrorMessage; }
}
You can create a validation rule this way:
class NotEmptyValidationRule extends ValidationRule {
public function validate($aForm) {
$fieldName = $this->fieldName();
$fieldValue = $aForm[$fieldName];
$passed = !empty($fieldValue);
$result = new ValidationRuleResult($passed);
if (!$passed) {
$result->setErrorMessage("$fieldName cannot be empty");
}
return $result;
}
}
Some things to note:
Im assuming that $aForm is an associative array of field name / value
You can note that if a validation rule passes, the result is not used (as the ValidationResult class works only on those results that didn't pass). Remember that this is a sample only for the purpose of helping you, is not a complete solution.
Usage
$rule = new NotEmptyValidationRule('name');
$validator = new FormValidator();
$validator->addRule($rule);
$aForm = $__POST['myForm'];
$validationResult = $validator->validate($aForm);
if ($validationResult->validationPassed()) {
$errorsFound = $validationResult->errorsFound();
// do something with the $errorMessage
$errorMessage = array_join('<br/>', $errorsFound);
}

What is the standard way to return errors to the user from a function?

What is the standard way to return errors to the user from a function? (email invalid, max characters exeeded, etc.)
function register($name, $email) {
if(!$name) {
$registration_errors = 'name empty,';
}
if(!email) {
$registration_errors = $errors . 'email empty,';
}
if($registration_errors) {
return $registration_errors;
}
else {
register stuff........
return true;
}
}
now the problem is that it always returns true so you cant do something like:
if(register()) {blah blah blah} else { put errors under inputs}
So what would be the standard method of doing this?
use something like this
function register
{
..
else
{
return array('error' => $errorwhichyoufound);
}
....
}
$result = register();
if((array_key_exists('error', $result)))
{
}
else
{
}
This way you can even check for individual errors.
<?php
// in your register function:
$registration_errors = array();
if (!$name) {
$registration_errors['name'] = 'Name is required.';
}
// etc.
?>
<!-- in your phtml: -->
<?php if (array_key_exists('name', $registration_errors) echo $registration_errors['name'];?>
<label for="name">Name</label>
<input id="name" name="name" type="text" value="<?php echo $name; ?>" />
There are all sorts of ways to do this, and you might even consider some of them "standard". You could return an array containing the error code (as the answers above), you could store the error as an object property (requiring you to call "register" as an object method), you could store the error in a global variable, or put the error in a registered location. You could pass in a variable reference and set the variable value to the error. You could reverse the return logic, so a string would be false and a zero would be a true value. You could raise an error. You could throw an exception.
The real problem here is that the "register" function is supposed to perform some function and you are asking it to sanitize the input just in case something funny is going on. So if you really need to sanitize $name and $email, why not do that validation before the call to register? You are going to need some kind of error path regardless, and perhaps several different error messages depending on the situation. When you are finally ready to call register, it should succeed or fail for its own reasons, and not because the business rules require a non-empty email address.

Categories