Tutorial Part 1 • Part 2 • Part 3 • Part 4 • Live demo
This tutorial part is about advanced custom validators and properties – and how to use them with a (pseudo-)ajax call.
More about custom validators
In Part 2 we created a custom validator, which tests for numbers and letters – a simple regular expression. But in real life there are often more complex criteria involved.
Let’s say in our registration form, we’d like each user to have a unique email-address. So the validator should not only check if the entered email address is valid (syntactically) but also query the (fictional) user database — to find out if the email is not yet registered.
Therefore we need the following:
- A page to tell us if a given email address is already registered
- A custom validator which sends the entered email address to this page
The page itself (formvalidator-ajax.php) is written in PHP and looks like this:
<?php
include('...'); // include the required framework classes etc.
$mail = isset($_REQUEST['mail']) ? $_REQUEST['mail'] : false;
if (User::emailExists($mail)) {
echo '1';
}
else {
echo '0';
}
?>
I won’t go into any details here since we’re talking about Mootools, not PHP – but simply put I assume a static method User::emailExists($which) exists, which checks if a given email address is already present in the database and returns true or false. Of course the input has to be filtered against injections etc. but this happens behind the scenes.
This simple page receives the parameter mail via GET or POST and returns “1” if the email exists, otherwise “0”.
So we can check the existence by calling http://yourdomain.org/formvalidator-ajax.php?mail=test@test.de
Now we build another custom validator, called “emailUnique”, which uses the Request()-class from Mootools to access this page:
FormValidator.add('emailUnique', {
errorMsg: 'E-Mail address is already registered',
test: function(element, props) {
if (element.value.length > 0) {
var req = new Request({
url: '/cwcrop/formvalidator-ajax.php',
async: false
}).send("mail=" + element.value);
return (req.response.text != '1');
}
return true;
}
});
Have a look at the test()-method:
It calls an URL synchronously (so ajax
is a bit misleading) and appends the parameter mail as GET-Parameter. The value is of course the content of the input field (element.value).
Since this request is synchronous we don’t have to use callbacks. We can directly handle the response from the requested page — if our response is not equal to “1” the validation was successful (=email can be used).
Error handling, security and convenience
A synchronous request has one big disadvantage: It waits for the request to end, so if the connection is extremly slow or times out, the script waits as long as this takes. In a high traffic environment you better use an asynchronous request.
Note the “return true;” at the end: This is the fallback. If the page does not return anything we optimistically assume the email address is valid.
But can’t this lead to problems?
No! Since it’s absolutely necessary to validate all input a second time via PHP before inserting it into the database! The FormValidator is convenient, but not secure (simply turn Javascript off …).
So our fallback does the right thing: It simply delays the validation to the PHP part and in the between time the user can go on.
If we had used “return false;” as fallback and the requested page had a problem, the user would always get an error message, regardless of her input – not a good way to convince user to register..
Using properties
If you have a user database and a registration form you surely will also have a “profile”-page, where a registered user can change her data.
This page is usually quite similar to a registration form with all data already filled in. But what about our emailUnique-validator? It doesn’t work – because if the user does not change the email address she gets an error, since her own email address is of course already present in the database.
But lazy as we are we don’t want to write a second validator, we want to reuse the existing one.
And this is also quite simple: We add a second parameter to the database query — the user-id. If there is no user-id then we are in the registration process and everything stays the same. But if a user-id is present (=the user is already in the database) we’re going to check the email against the database except the row with this user-id.
Email uniqueness v2
<?php
include('...'); // include the required framework classes etc.
$mail = isset($_REQUEST['mail']) ? $_REQUEST['mail'] : false;
$uid = isset($_REQUEST['uid']) ? $_REQUEST['uid'] : false;
if (User::emailExists($mail, $uid)) {
echo '1';
}
else {
echo '0';
}
?>
So we add a second parameter (uid, short for user-id) and the database query ignores the appropriate row — if we pass a valid user-id to this method.
And we want to recycle our validator on the profile page, so we need a way to pass an additional information to it: The user-id. That’s what properties are for.
Properties are simple key-value pairs which are written into the HTML class-attribute like this:
<input class="minLength:10">
In our case we add the property “userId”, so the email field on the profile page looks like this:
<input class="emailUnique userId:14 validate-email" type="text" name="profile[email]" value="any@any.org" />
Of course the number 14 and the value are dynamically written there by a PHP method.
And here’s how the validator looks with properties:
FormValidator.add('emailUnique', {
errorMsg: 'E-Mail address is already registered',
test: function(element, props) {
if (element.value.length > 0) {
var uid = '';
if ($type(props.userId)) {
uid = props.userId;
}
var req = new Request({
url: '/cwcrop/formvalidator-ajax.php',
async: false
}).send("mail=" + element.value + "&uid=" + uid);
return (req.response.text != '1');
}
return true;
}
});
So we append a second parameter (uid) to our request — which of course can be empty (that’s the case on the registration form.
The only thing we need to do is to look if this property is present and if so, read it. And therefore the second parameter of the test()-method is used – it’s an object with all properties present in the given element.
Okay: We look if the userId field is present in the properties and put it into the variable uid (which by default is empty) and append this to our URL. And ready.
The property values are JSON-like encoded, see the mootools documentation for more information.
Done (well not really)
Comments, remarks and criticism are welcome! But don’t miss the additional part 4 »