-
Notifications
You must be signed in to change notification settings - Fork 0
Extending custom rules
The built-in rules cover common HTML constraints, but real applications usually need a few rules that are specific to their own data and workflows.
Typical examples include:
- a password must not contain the username
- a username must not already exist
- one date must be later than another
- an invite code must match a value stored elsewhere
These are all beyond what a generic HTML attribute can express, which is why DomValidation lets us add our own rule objects.
Custom validation uses two related types:
-
Rule: a single validation rule -
ValidationRules: a collection of rules used by the validator
Most projects will start from DefaultValidationRules so that the standard HTML rules still apply.
This example makes a password invalid if it contains the submitted username.
use GT\Dom\Element;
use GT\DomValidation\Rule\Rule;
class UsernameNotWithinPassword extends Rule {
public function isValid(
Element $element,
string|array $value,
array $inputKvp,
):bool {
if($element->type !== "password") {
return true;
}
if(!is_string($value)) {
return true;
}
return !str_contains($value, $inputKvp["username"] ?? "");
}
public function getHint(Element $element, string $value):string {
return "The password must not contain the username";
}
}There are three things to notice here:
- The rule receives the current element.
- It also receives the current field value.
- It receives the whole submitted input array, which makes cross-field checks possible.
use GT\DomValidation\DefaultValidationRules;
$rules = new DefaultValidationRules();
$rules->addRule(new UsernameNotWithinPassword());Then pass that rule collection to the validator:
use GT\DomValidation\Validator;
$validator = new Validator($rules);use GT\Dom\HTMLDocument;
use GT\DomValidation\DefaultValidationRules;
use GT\DomValidation\ValidationException;
use GT\DomValidation\Validator;
$document = new HTMLDocument($html);
$form = $document->forms[0];
$rules = new DefaultValidationRules();
$rules->addRule(new UsernameNotWithinPassword());
$validator = new Validator($rules);
try {
$validator->validate($form, [
"username" => "cody",
"password" => "cody-password",
]);
}
catch(ValidationException) {
foreach($validator->getLastErrorList() as $name => $message) {
// Render the error message however the application prefers.
}
}Every rule may optionally return a more specific exception class.
If we do not override getExceptionClass(), the base Rule class returns ValidityStateException.
That is usually enough if we only care about the error messages. Override it when we want to distinguish one category of validation problem from another at exception level.
Try to keep each custom rule small and easy to name.
This is better:
UsernameNotWithinPasswordEndDateAfterStartDateInviteCodeIsKnown
Than one large rule object that tries to validate half the form at once.
Small rules are easier to test, easier to reuse, and easier to explain when someone reads the code later.
If you want to understand the exception types and grouped messages that come back from the validator, go to Understanding validation errors.
DomValidation is a separately maintained component of PHP.GT WebEngine.