• Status: Solved
  • Priority: High
  • Security: Private
  • Views: 76
  • Last Modified:

Creating my own form validation class

I have found that for forms on the website I am building, I keep writing the same validation code over and over again. So, I thought it might be a good idea to create a validation class. I am still learning OOP so please bear with me.

I must also mention that I am using a custom mini MVC framework.

Just to see if I could get it working,  I put 2 methods into my class:

class Validation {
	

	public function val_email($email)
	{
		if(filter_var($email, FILTER_VALIDATE_EMAIL)) {
			return true;
		}
	}
	
	public function is_empty($formval)
	{
		if(!empty(trim($formval))) {
			return true;
		}
	}
}

Open in new window


I then instantiate it in my controller :

      
public function test()
	{
		if($_SERVER['REQUEST_METHOD'] == 'POST') {
			
		$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
		$name = filter_var($_POST['name'], FILTER_SANITIZE_STRING);
			
		$data = [
			
			'email' => $email,
			'name' => $name,
			'message' => ''
		];
		
		
		$validation = new Validation;
			
		if(!$validation->val_email($email)) {
			
			$data['message'] .= 'Invalid email address <br />';
		}
	
		if(!$validation->is_empty($name)) {
			
			$data['message'] .= 'Name required <br />';
		}
			
		if(!empty($data['message'])) {
			
			$this->view('pages/test', $data);
			
		} else {
			
			die('okay');
		}
		
		$data = [
			
			'email' => $_POST['email'],
			'message' => ''
		];
		
		$this->view('pages/test', $data);
			
		}
		
		else {
			
			$data = [
				
				'email' => '',
				'name' => '',
				'message' => ''
			];
			
			$this->view('pages/test', $data);
		}
	}

Open in new window


This works in that if I submit the form, I get the validation errors until I fill out the form correctly. But I just wanted to know if what I am doing is correct. Just because it works doesn't mean it's right!

I do have another question as well though, it doesn't seem to work if I do the opposite and return false which is what I would prefer to do actually.

	public function is_empty($formval)
	{
		if(empty(trim($formval))) {
			
			return false;
		}
	}

Open in new window

0
Black Sulfur
Asked:
Black Sulfur
  • 9
  • 4
  • 3
2 Solutions
 
Julian HansenCommented:
But I just wanted to know if what I am doing is correct. Just because it works doesn't mean it's right!
Difficult to answer.

What I can tell you is that there are some standard ways of doing this. One practice is to use a rule based system where rules are specified in an Array - you specify the rule you want to run and the data it must run it against.

The problem I have found with these approaches is they work well in simple cases but tend to be a bit more complicated when you have situations where more complex validation.
For instance, if user clicks checkbox A then field B must have a value otherwise it can be empty - to give an example.

To accommodate such scenarios the solutions tend to become overly complicated trying to cater for every eventuality - to the point that the code you have to write for them is almost more complicated than if you were just to write the validation rules directly.

Most coders are lazy - if they have done something before they don't want to have to do it again so there is a natural tendency to want to write generic code - but one needs to keep in mind the problem you are trying to solve - writing the best generic validation code when you need to check an email address and name field are completed might not be the most optimal solution in terms of time and effort.

So, long story short, start out simple - use some simple validation routines that will validate various types of data and then build the code for your form that combines these using whatever other logic your form requires (for example cross dependence).
0
 
Black SulfurAuthor Commented:
Thanks, Julian. I wanted to use this for the more simple validation that I do over and over again, like for example validating an email address or name for example. I would't use it for a once off unique validation for a specific project. I jsut want to use it for more generic stuff. And it might have been nice to get the validation class to also output the actual error.
0
 
Black SulfurAuthor Commented:
Or are you saying I shouldn't have a validation class?
0
Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
gr8gonzoConsultantCommented:
One recommendation I'd make is to use static methods in your class.:

public static function val_email($email)

public static function is_empty($formval)

That way, you can just call them directly without creating an instance of your Validation class:

// Don't need to instantiate the class anymore ---  $validation = new Validation; 

if(!Validation::val_email($email))
{
  $data['message'] .= 'Invalid email address <br />';
}

if(!Validation::is_empty($name))
{
  $data['message'] .= 'Name required <br />';
}

Open in new window


Next, I'd suggest keeping an array of your messages, instead of appending to a string, and get rid of the <br /> breaks:
$validation_errors = array();
if(!Validation::val_email($email))
{
  $validation_errors[] = 'Invalid email address';
}

if(!Validation::is_empty($name))
{
  $validation_errors[] = 'Name required';
}

if(count($validation_errors))
{
  // Now assemble your final error message
  $data['message'] = implode("<br />", $validation_errors);
}

Open in new window

This is personal style preference, but I find that it's cleaner. That way you're not repeating "<br />" and making it a part of your actual error message (which is good if you ever want to apply this to an API where the client might not understand HTML, and instead you want to return an encoded array of error messages or something). Plus, you avoid the final <br /> at the end, which is usually unnecessary.

I agree with Julian that some of the validation rules can get complex, but resist the urge to over-engineer. Sometimes, repeated code is actually a good thing. Let's say you have 5 forms and you build out a super validation class that handles all the validation in a few lines of code and it works great for months. Then you get a request to build a new form that has to be very different from the others and is validated a little differently.

Now you might end up having to spend MORE time in the long run trying to tweak the super validation class to handle all the variations of all the forms that come up instead of just spending 5 minutes tweaking some repeated code.

It's a good thing to have your own validation class, but treat it like a real-life toolbox that has lots of different tools that each do one thing well. There's a reason that Home Depot sells a thousand varieties of screwdrivers and knives instead of just selling Swiss army knives.

Also, don't reinvent the wheel for the small things. For example:
        public function is_empty($formval)
	{
		if(!empty(trim($formval))) {
			return true;
		}
	}

Open in new window

I would say you don't really need this function at all. The only purpose is to save you from an extra trim() call, but it's almost always a safe bet that you want to trim() all of your incoming form data. So it might be better to have something like:
public static function TrimArray($arr)
{
  foreach($arr as $k => $v)
  {
    $arr[$k] = trim($v);
  }
}

Open in new window


Then before you do any validation, just run:

$_POST = Validation::TrimArray($_POST);

Now you can just check empty() directly:

if(empty($name))
{
  $validation_errors[] = 'Name required';
}

Open in new window


You could also have some functions that sanitize multiple fields quickly. Here's my take on a useful validation class:

<?php
class Validation
{
  // Trim all elements in the given array
  public static function TrimArray($arr)
  {
    foreach($arr as $k => $v)
    {
      $arr[$k] = trim($v);
    }
    return $arr;
  }
  
  public static function SanitizeEmails(&$arr)
  {
     // Handle dynamic number of arguments
     $fields = func_get_args(); array_shift($fields);
     
     // Call the Sanitize routine
    self::Sanitize($arr, $fields, FILTER_SANITIZE_EMAIL);
  }
  
  public static function SanitizeStrings(&$arr)
  {
     // Handle dynamic number of arguments
     $fields = func_get_args(); array_shift($fields);

     // Call the Sanitize routine
    self::Sanitize($arr, $fields, FILTER_SANITIZE_STRING);
  }
  
  public static function Sanitize(&$arr, $fields, $method)
  {
    // Walk through the array and run filter_var() using the requested $method
    foreach($arr as $k => $v)
    {
      if(in_array($k, $fields))
      {
        $arr[$k] = filter_var($v, $method);
      }
    }
  
    // Fill in any missing / expected fields as null
    foreach($fields as $field)
    {
      if(!isset($arr[$field])) { $arr[$field] = null; }
    }
  }
  
  public static function Require($arr, $fields)
  {
    $messages = array();
    foreach($fields as $field => $name)
    {
      if(empty($arr[$field]))
      {
        $messages[] = $name . " is required!";
      }
    }
    return $messages;
  }
}

Open in new window


This way, you'll end up with ONLY the variables you've sanitized and expected to receive, and you'll properly overwrite any other values that might come in from other, unexpected / unvalidated sources. Usage example:
<?php
// Sample input
$_POST = array("hello" => "world", "email" => "test@gmail.com ", "name" => "gr8gonzo");

// Trim everything first
$_POST = Validation::TrimArray($_POST);

// Sanitize your data
Validation::SanitizeEmails($_POST, "email");
Validation::SanitizeStrings($_POST, "name", "address", "city", "state", "hello");

// Now perform your initial required-field check validation
$messages = Validation::Require($_POST, array(
  "email" => "E-mail Address", 
  "name" => "Name"));

// And finally any additional specific validation here
if($_POST["hello"] != "world")
{
  $messages[] = "The 'hello' input should always have a value of 'world' !";
}

// Now check to see if you have any errors
if(count($messages))
{
  // At least one validation error
  ... display the errors ...
}
else
{
  // All validation passed
}

Open in new window

0
 
Black SulfurAuthor Commented:
Thanks for that gr8gonzo, I will try this out when I get home from work later today.
0
 
Black SulfurAuthor Commented:
gr8gonzo, after reading you post a few times I think I am more confused than when I started, haha :)

I want to just take baby steps here as that might make things easier for me.

Firstly, I am having trouble with the actual class. Validation only fails if I make the method return true, e.g.:

	public static function val_email($email)
	{
		if(filter_var($email, FILTER_VALIDATE_EMAIL)) {
			
				return true;
		}
	}

Open in new window


Then in the controller I have:

$validation_errors = array();

if(!Validation::val_email($email))
			
		{
		  $validation_errors[] = 'Invalid email address';
		}

		if(count($validation_errors)) {
		
		$data['message'] = implode("<br/>", $validation_errors);
		
		}

                // if validation fails, send $data array to the view
		if(!empty($data['message'])) {
			
			$this->view('pages/test', $data);
			
		} else {
			
			die('okay');
		}

Open in new window


That works okay. I get the validation error if I don't input a proper email address. Why does this not work if I make the method return false? That to me seems to make sense. If something returns false, show an error. But I can't seem to get it to work like that.

If I do this:

      
public static function val_email($email)
	{
		if(filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
			
				return false;
		}
	}

Open in new window


Then the validation error displays whether I enter a valid email address or not.
0
 
Julian HansenCommented:
Here is a simple validation solution
I have created 3 files
HTML: This demonstrates a simple form requesting validation from the server by AJAX
Server Action Script: Processes the POST, builds the validation rules and calls the validation function
Validation Script: A script that takes rules and processes the data based on those Rules

There is also an API Tester that can be used to test other combinations of data - I will expand on this later
HTML
A simple form with two fields: email and firstname.
For ease of processing I am using array notation for the names - you will see why in the next two scripts
<form>
<div class="form-group">
  <input type="text" id="email" name="data[email]" class="form-control" placeholder="Email address"/>
  <div class="alert alert-danger error"></div>
</div>
<div class="form-group">
  <input type="text" id="firstname" name="data[firstname]" class="form-control" placeholder="First name"/>
  <div class="alert alert-danger error"></div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>

Open in new window

Action Script
Accepts the POST and sends it to validation with appropriate rules
<?php
require_once('t3012.validate.php');

// Prepare our response
$result = new stdClass;
$result->status = false;

// Get the data: this is why we use array notation
// We get the entire form contents in one go AND
// we are able to separate our form data from any
// orther control / options POST data that might
// need to be sent
$data = isset($_POST['data']) ? $_POST['data'] : false;


if ($data) {
  // Setup the rules
  // Index is the id of the field we are validating
  // Item is an array: 
  //    type(required) specifies the validation to perform
  //    The remaining paramters are dependent on the validation type
  $rules = array(
    'email' => array('type' => 'email', 'msg' => 'A valid email address is required'),
    'firstname' => array('type' => 'regx', 'pattern' => '/^[a-zA-Z]{5,100}$/', 'msg' => 'A firstname is required')
  );

  // The validate() function returns an array of errors found
  // based on the rules - we are going to return these as is
  $result->errors = validate($data, $rules);
  
  // The status of this validation is based on how many errors
  // we found. 0 means success, > 0 errors were found
  $result->status = empty($result->errors);
}
else {
  // This is to handle the case when we don't receive valid
  // data in the post
  die('invalid action');
}

// Send the results back to the client
die(json_encode($result));

Open in new window

Validation Script
Contains the code to do the actual validation
<?php
// The validatation workhorse
// Parameters:
//   data(required)  - the array of data items to validate
//   rules(required) - an array of rules to apply to the data
function validate($data, $rules)
{
  // Our return
  $errors = array();
  
  // Loop through rules - we don't care about data
  // items not in the rule array
  foreach($rules as $k => $r) {
    // Check the item exists
    $valid = isset($data[$k]);
    
    // If it does validate it
    if ($valid) {
      // Build our dynamic action name based 
      // on the type
      $action = "validate_{$r['type']}";
      
      // If the method exists then call it with
      // this data item and the rule for it
      if (function_exists($action)) {
        $valid = $action($data[$k], $r);
      }
    }
    // If the validation failed or item does not exist
    // set the error message for that item
    if (!$valid) {
      $errors[$k] = $r['msg'];
    }
  }
  
  return $errors;
}

// Validation functions

function validate_email($email, $rule)
{
  return filter_var($email, FILTER_VALIDATE_EMAIL);
}

function validate_regx($data, $rule)
{
  return preg_match($rule['pattern'], $data);
}

Open in new window

Working sample that uses the above here

The API Tester http://www.marcorpsa.com/apitester

This tool allows you to test API calls using a variety of different parameters
For this enter the following
API URL
http://www.marcorpsa.com/ee/t3012.php
Method
POST
Return Type
JSON
Send Type
URL
Parameters
data[email]=invalidemail&data[firstname]=bob

Click Run to view results in the top right panel
Play with the above values to test the response from the server
0
 
gr8gonzoConsultantCommented:
The problem with your validation is that you are not returning a true when it's successful:

      public static function val_email($email)
      {
            if(filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
                        return false;
            }
      }

So in a VALID email scenario, the function won't return ANYTHING (which basically means it's returning a null). So if you take this line:

if(!Validation::val_email($email))

and think about the return values in each scenario, it's basically the equivalent of this:

INVALID EMAIL:
if(false == false)

VALID EMAIL:
if(null == false)

PHP is very loose with some concepts, especially when comparing data types, and tries to handle a lot of situations for you, and sometimes it does a bad job. This is a good example of that.

PHP believes that null == false.
PHP also believes that 0 == false.
PHP also believes that false == false.

It's essentially trying to make assumptions about what you meant instead of just throwing a warning or an error message.

So the solution here is to return true to your function if it doesn't return false:

      public static function val_email($email)
      {
            if(filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
                        return false;
            }
           else
            {
                        return true;
            }
     }
1
 
Black SulfurAuthor Commented:
Thanks to you both for your posts which are very comprehensive. But if you have the patience to bear with me, I want to just break this down into smaller pieces as the main goal of this isn't for me to actually build a validation class. It has been said on this thread and I have read in a few different places around the net that creating a validation class is a bad idea, and I have also seen suggestions that using value objects would be better ( I still have to look this up as I don't know what that is).

Anyway, my main objective here is to get the class to interact with my controller, if that is the right way of doing this. So, in my validation class, all I have is this:

class Validation {
	
	
      public function val_email($email)
      {
            if(filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
				
            return false;
				
	  } else {
				
             return true;
				
            }
     }
}

Open in new window


I am going to leave out the static bit for now as I really want to go as basic as possible here just to grasp it and then I can play around with it and make it more complex and so on.

In my controller I have just instantiated the class:

$validation = new Validation;

	if(!$validation->val_email($email)) {
			
		$data['message'] = 'Invalid email address';
	}

		if(!empty($data['message'])) {
			
                        // send errors to the view
			$this->view('pages/test', $data);
			
		} else {
			
                        //successful validation
			die('okay');
		}

Open in new window


After reading your posts, it makes more sense to put the errors into an array, but again, I am just trying to make sure I understand the creating a class, instantiating it and then using the created object correctly.

So, my focus is mainly around this code:

	if(!$validation->val_email($email)) {
			
		$data['message'] = 'Invalid email address';
	}

Open in new window


I apologise if this is a really contrived example, but I need to grasp the very basics of this in order to see the bigger picture.
0
 
gr8gonzoConsultantCommented:
Your example looks fine - is there a specific problem at this point with that new code?
0
 
Black SulfurAuthor Commented:
@ gr8gonzo, no problem at this point. I am just glad that the way I have done it is correct, even if it is pointless. I just wanted to get the principle right in terms of creating the class and instantiating it in the controller.

I would have to expand on it to work like your and Julian's example for it to be worthwhile. But, my last question before I close this question out is, should I bother trying to start a validation class and building it up over time for convenience  on simple validation like email, names, dates etc. or should I just keep validating  in the controller as I have been up until now?

Eg:

      
	if(empty($name)) {
			
			$data['message'] .= 'Name is required <br />';
		}
			
		if(filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
			
			$data['message'] .= 'Invalid email address <br />';
		}
		
		if(!empty($data['message'])) {
			
			$this->view('pages/test', $data);
			
		} else {
			
			die('okay');
		}

Open in new window

0
 
gr8gonzoConsultantCommented:
Personally, I think a validation class has a lot of merit. It eliminates the chances of small mistakes when you're copying validation logic around.

If you're striving for as pure of an MVC model as possible, then really ALL of the validation should be done by models. The controller should really just start the validation call on the model and get back the results. Controllers are not the correct place for input validation logic.

That said, I tend to see MVC as a "take-what-you-need" type of system where you try to adhere as close as possible but make exceptions as necessary. You really have to think about how it might change in the future and be maintained over time, and decide for yourself if it's worth setting up different models for each validation scenario.
0
 
Black SulfurAuthor Commented:
Interesting. I am new to MVC but thought you would want to validate your data before sending it to the model. My models are currently strictly database transactions.
0
 
Julian HansenCommented:
Take a look at my last post - it is a complete validation system that you can use almost out of the box (and extend if you need).
0
 
Black SulfurAuthor Commented:
Thanks Julian, I will just have to try get it to work in my current MVC structure and figure out which part should be the class etc. But I suppose that I should also be careful not to change things for the sake of it. But that is one of the things I find most confusing about php. There are a lot of ways to do things and I already have something that works. But that's the catch, just because something works doesn't mean it is the right way to do it! So, I don't want to get used to doing something and it's bad practice or wrong etc. Still a lot to learn! Thanks guys for your valuable input. I will go through all of this and try implement into my existing framework.
0
 
Black SulfurAuthor Commented:
Thanks for both of your time, it is much appreciated.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 9
  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now