Page 1 of 2
Form Validation
Posted: Thu Aug 03, 2006 5:25 pm
by Luke
There has to be a better way to validate forms than this:
Code: Select all
// Title Valitation
$title = $Filter->stripTags($request->get('title'));
$errors['title'] = $Filter->checkLength($title, $title_max_length, 1) ? false : "Title Must be between 1 and " . $title_max_length . " characters in length.";
$Template->assign('title_error', $errors['title']);
$Template->assign('title', $title);
// Body Valitation
$body = $request->get('body');
$errors['body'] = $Filter->checkLength($body, $body_max_length, 1) ? false : "Body Must be between 1 and " . $body_max_length . " characters in length.";
$Template->assign('body_error', $errors['body']);
$Template->assign('body', $body);
Any advice on validating forms and reporting errors back to the user would be GREATLY appreciated, as this is what I always do (I haven't used objects in the past, but this method really is just procedural code with objects)
Thanks!

Posted: Thu Aug 03, 2006 5:32 pm
by Ambush Commander
Encapsulate the actual form post in an object.
Posted: Thu Aug 03, 2006 5:33 pm
by Luke
Elaborate... a specific class for every form? A form class with children classes per form?
Posted: Thu Aug 03, 2006 5:39 pm
by Ambush Commander
It will look like this when it's done:
Code: Select all
$form = new Form($_POST);
$status = $form->validate();
if ($status) {
// display success
} else {
// redisplay the form
$form_printer->paint($form);
}
Internally speaking, validate() might look like this:
Code: Select all
// retrieve the values
foreach ($this->fields as $name) {
$this->values[$name] = isset($post[$name]) ? $post[$name] : $this->defaults[$name];
}
// validate the values
$status = true;
foreach ($this->values as $name => $value) {
// only validate if there's a validator present
if (isset($this->validators[$name])) {
$validity = $this->validators[$name]->validate($value);
if (!$validity->isValid()) $status = false;
$this->errors = array_merge($this->errors, $validity->getErrors());
}
}
return $status;
I'll leave the rest of the details to your imagination.
Posted: Thu Aug 03, 2006 5:41 pm
by Luke
thanks, that makes a lot of sense!
Posted: Thu Aug 03, 2006 5:43 pm
by volka
Posted: Thu Aug 03, 2006 6:26 pm
by Luke
Anybody have any other methods?
Posted: Thu Aug 03, 2006 7:35 pm
by Ollie Saunders
OsisForms!
xD
With that you would be able to do something like:
Code: Select all
$f = new OF_Form('f');
{
$title = new OF_SmallText('title', 'Title');
$title->addFilter('noTags');
$body = new OF_LargeText('body', 'Body');
$body->width = 40; $body->height = 10;
$f->addEntities($title, $body);
}
if ($f->submitted()) {
foreach (array('title', 'body') as $fieldName) {
$length = $fieldName . '_max_length';
if (!$$fieldName->hasLengthBetween(1, $$length)) {
$$fieldName->addError(ucfirst($fieldName) . ' Must be between 1 and ' . $$length . ' characters in length.');
}
}
if ($f->getNumErrors()) {
// errors
} else {
// no errors
}
}
$Template->assign('form', $f);
But I need help finishing writing it first

Posted: Thu Aug 03, 2006 8:13 pm
by Luke
Here is what I did... and once again (I find myself doing this often) I am wondering if I missed the point:
Form.inc.php
Code: Select all
<?php
require('Validator.inc.php');
class Form{
private
$data = array(),
$validators = array(),
$errors = array();
public function __construct($data){
$this->data = $data;
}
public function addValidator($name, $rule, $error){
if(array_key_exists($name, $this->data)){
$validator = new Validator($name, $rule, $error);
$this->validators[$name] = $validator;
}
}
public function validate(){
foreach($this->validators as $name => $validator){
if(!$validator->isValid()) $this->errors[$name] = $validator->getError();
}
return empty($this->errors);
}
public function getErrors(){
return $this->errors;
}
}
?>
Validator.inc.php
Code: Select all
<?php
class Validator{
private
$error,
$name,
$rule;
public function __construct($name, $rule, $error){
$this->name = $name;
$this->rule = $rule;
$this->error = $error;
}
public function isValid(){
return $this->rule;
}
public function getError(){
return $this->error;
}
}
?>
Code: Select all
$Form = $Registry->get('addform');
$Form->addValidator('body',
$Filter->checkLength($request->get('body'), $body_max_length, 1),
"Must be between 1 and " . $body_max_length . " characters in length."
);
if($Form->validate()) echo "Form is valid";
Posted: Thu Aug 03, 2006 8:19 pm
by Ollie Saunders
Code: Select all
public function addValidator($name, $rule, $error){
if(array_key_exists($name, $this->data)){
$validator = new Validator($name, $rule, $error);
$this->validators[$name] = $validator;
}
}
Personally wouldn't do that. What if you have validations dependent on other validations, or the database, or the user permissions? You can't hope to successfully abstract validation logic without making things really complicated.
Posted: Thu Aug 03, 2006 8:21 pm
by Luke
ole wrote:Code: Select all
public function addValidator($name, $rule, $error){
if(array_key_exists($name, $this->data)){
$validator = new Validator($name, $rule, $error);
$this->validators[$name] = $validator;
}
}
Personally wouldn't do that. What if you have validations dependent on other validations, or the database, or the user permissions? You can't hope to successfully abstract validation logic without making things really complicated.
don't follow you
But... I would like to know how I could send the validator a rule to follow... how could I do that?
Posted: Thu Aug 03, 2006 8:38 pm
by Ollie Saunders
Well say you have a form for booking what you are going to eat at a resturant.
You can choose a starter, main and dessert from 3 <select> boxes. Above these selects are a tick boxes which you can tick or untick to specify whether you are going to have that particular the course. Because some people may not want a starter or dessert. Obviously if a user ticks that they want a dessert you have to validate that they have choosen from one of the available dessert options in the select and not just given you any old value so that is a 'Validation Rule' but the existance of that rule is conditional. You only need to do that check if they have choosen they want a dessert in the first place with the tick box.
That was a really simple example, these things can actually get quite a lot more complex if you are building web applications.
In OsisForms I solve this by providing a simple set of methods for validation but not by abstracting the validation itself. This way you still use logic to make the validations, which means you can achieve conditional validations and in fact pretty much anything but much the validation easier and more readible.
Code: Select all
if ($f->submitted()) {
if ($chkDessert->getInput()) {
if (!$selectDessert->isOneOf()) {
$selectDessert->addError('You must choose from one of the available options');
}
} else {
// don't bother =xD
}
}
Posted: Thu Aug 03, 2006 8:51 pm
by Luke

my brain hurts... I'm gonna go play my guitar for a while.
I'll be back.
Posted: Thu Aug 03, 2006 8:57 pm
by Christopher
I am not sure if you have looked at the
Skeleton code, but there are examples of Validator/Rule and FilterChain/Filter classes for this. There are also examples of a generic Input Controller class and state/transition based Application Controller -- and an example Form Controller built on top of it. There are code examples using these as well showing how to do form validation and display error messages for field errors.
Posted: Thu Aug 03, 2006 9:00 pm
by Luke
oh yea... I forgot about those. I'll take a look in a minute.
Arborint... if your code was commented and PHP5, I would love you. hehe... it's great and I borrowed about 100 things from it, but it takes me a long time to figure stuff out because it isn't commented.
