Advanced Form Validation

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
quocbao
Forum Commoner
Posts: 59
Joined: Sat Feb 04, 2006 2:03 am
Location: HCM,Vietnam
Contact:

Advanced Form Validation

Post by quocbao »

i am thinking about a way to make form validation easier .

i know there's a good ( very good ) class about form validation on PHPClasses (http://www.phpclasses.org/browse/package/1.html)
But using it's very complicated ( for designer ) :( , and it's almost impossible to seperate between designer and programmer ( i'm working with a group )

So we need a more simple solution :?: , anyone has an idea ?
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Well, I'd say this is one of those things you'd go build yourself.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

I have used the Rules bases Validator class approach to Forms validation for a long time and have yet to find something better. Those classes (along with Filter/Filterchain classes) are the building blocks of a Form Controller. Are you looking for a pre-written solution, or as Ambush Commander recommends -- building something yourself?
(#10850)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

http://www.phppatterns.com/docs/design/strategy_pattern might be worthwile to take a look at.
I have used the Rules bases Validator class approach to Forms validation for a long time and have yet to find something better
Arborint, do you have any good resources about that or a short explanation? I'm working on my own validation class based on the strategy pattern mentioned above, but am interested in what else is possible.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

matthijs wrote:Arborint, do you have any good resources about that or a short explanation? I'm working on my own validation class based on the strategy pattern mentioned above, but am interested in what else is possible.
That article is the long version of the code that I use. It is an interesting study in the change that has happened in the last few years from using inheritence (like the article uses) to composition (like the code below).

The interesting thing about the Validator is the recognition that you are looking for error messages because that's what you want to display. If there are no error messages you are done. But it is the pluggable rules that makes the thing so flexible. Fabulous polymorphism at work.

Here is a Rule based version:

Code: Select all

class Validator {
var $chain = array();
var $errorMsg = array();

function addRule(&$rule) {
	if (is_array($rule)) {
		$this->chain = array_merge($this->chain, $rule);
	} else {
		$this->chain[] = $rule;
	}
}
	
function validate ($request) {
	$this->errorMsg = array();
	foreach ($this->chain as $rule) {
		if (! $rule->isValid($request)) {
			$this->errorMsg[] = $rule->getErrorMsg();
		}
	}
	return $this->isValid();
}

function isValid() {
	return empty($this->errorMsg);
}

function getErrorMsg() {
	return $this->errorMsg;
}

} // end class Validator



class Rule {
    var $field;
    var $errorMsg;

    function getErrorMsg() {
      return $this->errorMsg;
    }
}

class RuleNotNull extends Rule {

	function RuleNotNull($field, $errorMsg) {
		$this->field    = $field;
		$this->errorMsg = $errorMsg;
	}
	
	function isValid($request) {
		$value = $equest[$this->field];
		return ($value != '');
	}
}
And is used like this:

Code: Select all

$validator = new Validator();
$validator->addRule(new RuleNotNull('username', 'Username required'));
$validator->addRule(new RuleNotNull('password', 'Password required'));
$validator->validate($_POST);
if ($validator->isValid()) {
    // form is valid
    header('Location: form_done.php');
} else {
    // get error messages
    $errormsg = 'Errors: ' . implode(', ', $validator->getErrorMsg());
    // show form and error message
}
This is a cut down version that uses a Request array rather than a Request container object and only has one simple rule. The power is that you can create as many different rules as you want and add them in any combination. Each form field might have several rules. So for example if you also want to make sure that the Username was at least 3 characters long and the Password was at least 7 characters long, you would just add more rules for with those keys. You would need to build a RuleStrLength class that extends Rule for that.

This Validator runs after the FilterChain. I usually create a Parameter class to hold the filters, rules, and other data about each form field that the form controller then uses.
(#10850)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

Christopher, thanks a lot for your explanation. Although the details are a bit difficult to me, I understand the overall view.

Looks like a flexible approach to me. At first sight it would look a bit cumbersome, to write a rule for every Rule for every variable-to-validate. But at the same time that gives the approach it's flexibility, isn't it?

In a lot of validation classes the usage is something like:

Code: Select all

$validate->validateString("$_POST['name']", "3", "20", text, y);
in which the different arguments represent things like what to validate, minimal length, maxlength, kind of string, obligatory, etc etc.

But thinking about it, this approach is less flexible because the amount and kind of arguments are hardcoded in the class. When a somewhat different set of validation rules is needed in a certain situation one has to rewrite the code in the class. With your appraoch you have a set of classes, and you can call all the Rules you need for the specific validation of a variable.

I do have some things I do not understand:
This is a cut down version that uses a Request array rather than a Request container object and only has one simple rule
and
This Validator runs after the FilterChain. I usually create a Parameter class to hold the filters, rules, and other data about each form field that the form controller then uses
But I guess I'll have to read up some more about design patterns to understand this, isn't it?
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

matthijs wrote:Christopher, thanks a lot for your explanation. Although the details are a bit difficult to me, I understand the overall view.

Looks like a flexible approach to me. At first sight it would look a bit cumbersome, to write a rule for every Rule for every variable-to-validate. But at the same time that gives the approach it's flexibility, isn't it?

In a lot of validation classes the usage is something like:

Code: Select all

$validate->validateString("$_POST['name']", "3", "20", text, y);
in which the different arguments represent things like what to validate, minimal length, maxlength, kind of string, obligatory, etc etc.

But thinking about it, this approach is less flexible because the amount and kind of arguments are hardcoded in the class. When a somewhat different set of validation rules is needed in a certain situation one has to rewrite the code in the class. With your appraoch you have a set of classes, and you can call all the Rules you need for the specific validation of a variable.
You don't need to write a rule for every variable -- once you get a set of standard ones the you just reuse them. For checking length you would create a rule like this:

Code: Select all

class RuleLength extends Rule {
	var $min;
	var $max;
	
	function RuleLength($field, $min, $max, $errorMsg) {
		$this->field= $field;
		$this->min= $min;
		$this->max= $max;
		$this->errorMsg = $errorMsg;
	}
	
	function isValid($request) {
		$value = strlen($request[$this->field]);
		
		return ($this->min <= $value && $value <= $this->max);
	}
}
So to extend the example above you could do:

Code: Select all

$validator = new Validator();
$validator->addRule(new RuleNotNull('username', 'Username required'));
$validator->addRule(new RuleLength('username', 3, 30, 'Username must be 3 to 20 characters long'));
$validator->validate($_POST);
Now the will get both error messages if they type nothing, but only the length error if the type 1, 2 or more than 20 characters. And you can keep reusing your pool of Rule classes on other fields, but you are free to write an occasional custom one as well.
matthijs wrote:I do have some things I do not understand:
This is a cut down version that uses a Request array rather than a Request container object and only has one simple rule
and
I think it is pretty common to use a standard Container type object to encaptsulate data that is like an associative array, but needs some more logic -- like the Request. It usually has get(), set() and has() methods. But Containers are separate and bigger subject.
matthijs wrote:
This Validator runs after the FilterChain. I usually create a Parameter class to hold the filters, rules, and other data about each form field that the form controller then uses
But I guess I'll have to read up some more about design patterns to understand this, isn't it?
Yeah ... the idea is to build up larger structures, like a Form Controller, from simpler, testable classes like these. If you want more specifics I could show examples of some of these classes.
(#10850)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

Thanks again for your answer Christopher. You RuleLength example makes a lot of sense.

I'll definately go and try to extend this. I know how to write the basic validation rules, but to see it implemented in a structure like this is great. If you find the time and feel like it: I would certainly be interested in more examples.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

I sent you a link to some example code. If you think it is interesting or useful I can post it here.

If you want more examples, just be specific and I can provide them.
(#10850)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

Thanks, I pm'd you as well. If I'm having some trouble somewere I'll let you know.
User avatar
quocbao
Forum Commoner
Posts: 59
Joined: Sat Feb 04, 2006 2:03 am
Location: HCM,Vietnam
Contact:

Post by quocbao »

Thanks all for replying
I learn from you a lot :)

Now , new approach

I also want to use both javascript and php to validate user input :oops:

Do you have an other solution too ?
Or there is some pre-made package ? ( yes , http://www.phpclasses.org/browse/package/1.html , but again , i don't want to use this class )

Thanks all for helping me :D
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

Adding client side validation is not a bad idea. There are many examples and scripts out there. However, remember to always start with a solid serverside validation. Javascript can always be disabled. Sometimes on purpose, other times someone accessing your site has no other choice. This is also important for the accessibility of your forms/applications.

So, my advice is to take two terms into account: progressive enhancement and gracefull degradation. If you want to read more about this: If you would like to see some examples of javascript form validation: An approach I really would like to take myself is combine both sides in some way. Because building and maintaining both serverside and clientside validation routines is cumbersome. I think 2 approaches are possible here: first is the idea to let the PHP output the js, depending on which form fields and validation rules are needed. So you only have to write the php rules, and the class/package takes care of everything.

The other possibility is one that Chris Campbell mentions in his article: using hidden form fields which tell the server what type of validation to perform. That way, you don't have to write the client side validation routines seperately. Only include a common js validation class in the head of the document, and add the hidden input fields to the form. (which can easily be outputted dynamicly by a php script of course). If js is disabled the form is posted normally and server side validation takes place. If js is enabled, the js performs the client side validation first.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

quocbao wrote:I also want to use both javascript and php to validate user input :oops:
If you look at the Validator code above, it should be posible to write a mirror version in Javascript and the generate a CheckFrom() javascript function that would run in the form's onSubmit. Something like this pseudocode:

Code: Select all

function CheckForm(form) {
    validator = new Validator();

// have PHP generate these lines
    validator.addRule(new RuleNotNull('username', 'Username required'));
    validator.addRule(new RuleNotNull('password', 'Password required'));

    validator.validate(form);
    if (validator.isValid()) {
        return true;
    } else {
        alert('Errors: ' + validator.getErrorMsg());
        return false;
    }
}
The next step might be to break this down into a field by field validation.
(#10850)
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

I don't know about the strategy pattern, having a class for every field that requires validation seems kind of redundant doesn't it?
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Jcart wrote:I don't know about the strategy pattern, having a class for every field that requires validation seems kind of redundant doesn't it?
Which part is reduntant? Do you mean a "class" for every field or an "object" for every field? Are you talking about the PHP side or the Javascipt side?

The thing about "fields" for me is that they potenially need a bunch of stuff for them. For a form each data field needs a one or more filters, one or more validators, plus error messages, and potentially one or more callbacks that run on some state change (load/submit/accepted). That's just the data fields. If you think about a general architecture -- a parameter could be an Event that triggers an action. The point of a controller is to create a reusable system rather than custom coding every form. And for me encapsulating all the stuff for each potential parameter makes sense. The question is how to do it.
(#10850)
Post Reply