Skip to main content

Custom Validation Rules

To register a custom validation rule you must use the register method.

    const { register } = require('simple-body-validator');
    import { register } from 'simple-body-validator';

The register method takes three parameters:

  1. rule: The name of the rule.
  2. validate: A function that takes the attribute value and returns true if the validation is successful, or false otherwise.
  3. replaceMessage: Optional parameter to replace placeholders in the error message

Register method signature
register(
rule: string,
validate: (value: any, parameters?: string[], attribute?: string) => boolean,
replaceMessage?: (
message: string, paramters: string[], data?: object
) => string
): boolean;

Here is an example of a simple custom validation rule:

register('telephone', function (value) {
return /^\d{3}-\d{3}-\d{4}$/.test(value);
});

You can use the telephone rule in your validation rules:

import { make } from 'simple-body-validator';

const validator = make()
.setData({ cell: '+961 123 456 7890' })
.setRules({ cell: 'required|telephone'});

caution

You cannot register a rule that already exists. For example, you cannot register a rule named required since it already exists in the validation rules.

You can also specify the error message that should be returned in case the custom rule fails, by adding a new key value to the translation object.

import { setTranslationObject } from 'simple-body-validator';

setTranslationObject({
en: {
telephone: 'The :attribute phone number is not in the format XXX-XXX-XXXX.',
}
});

If you are not familiar on how to add error messages you can read about it here.

tip

The :attribute placeholder will be replaced by the actual name of the field under validation.

Accessing Additional Data In Your Custom Rule

If your registered validation rule needs to access all the other data undergoing validation. You can use the this.data attribute.

    register('custom_rule', function (value) {
console.log(this.data);
});

Replace Placeholders in the Error Message

In case you have a more complex use case, and you want to replace placeholders in the error message you can pass a method as a third parameter to the register method.

Let's say we want to register a new rule called complex_telephone. We will first start by adding the error message for that rule.

import { setTranslationObject } from 'simple-body-validator';

setTranslationObject({
en: {
complex_telephone: 'The :attribute phone number is not in the format +:code XXX-XXX-XXXX.',
}
});

In the case of the complex_telephone rule we would like to replace the :code placeholder dynamically.

register('complex_telephone', function (value, parameters) {
const [ code ] = parameters;
const pattern = new RegExp('^\\+' + code + ' \\d{3}-\\d{3}-\\d{4}$');
return pattern.test(value);
}, function(message, paramters) {
const [ code ] = paramters;
// replace the code in the message with the code sent in the parameters
return message.replace(':code', code);
});

Implicit Custom Rules

By default, custom validation rules are not run if the attribute is not present or contains an empty string. To register a custom rule that runs even when the attribute is empty, you can use the registerImplicit() method.

The registerImplicit() method takes the same parameters as the register() method.

const { registerImplicit } = require('simple-body-validator');
import { registerImplicit } from 'simple-body-validator';

Here is an example of an implicit custom validation rule:

registerImplicit('required_if_type', function(value, parameters) {
const [ target, type ] = parameters;

if (typeof this.data[target] === type) {
// This a built in method that is actually used for the required rule
return this.validateRequired(value);
}

return true;
});

You can then use the required_if_type rule in your validation rules:

const validator = make()
.setData({ first: 'test' })
.setRule({ last: 'required_if_type:first,string' });

Custom Validation Rule Classes

You can also create custom validation rule classes. A custom validation rule class must extend the Rule class, which has two methods

  • passes(): This method takes the attribute value and returns true if the validation is successful, or false otherwise.
  • getMessage(): This method returns the error message that should be used when validation fails.
const { Rule } = require('simple-body-validator');
import { Rule } from 'simple-body-validator';

Here is an example of a custom validation rule class:

class UpperCase extends Rule {
passes(value) {
if (typeof value != 'string') {
return false;
}

return value.toUpperCase() === value;
}

getMessage() {
return 'The :attribute must be uppercase.';
}
};

You can then use the custom validation rule class:

const validator = make(data, {
name: [ 'required', 'string', new UpperCase ],
});

If you wish to have translation for the UpperCase rule your can add a key value to the translation object and use the trans method in the getMessage method.

import { setTranslationObject } from 'simple-body-validator';

setTranslationObject({
en: {
uppercase: 'The :attribute must be uppercase.',
}
});

The getMessage implementation becomes as follows.

getMessage() {
return this.trans('uppercase');
}

Create a Custom Implicit Rule Using a Class

If you want the validation to run even when the field is not available or empty you can use ImplicitRule object.

const { ImplicitRule } = require('simple-body-validator');
import { ImplicitRule } from 'simple-body-validator';

Below we will showcase a simple example on how to register an implicit rule.

const { ImplicitRule } = require('simple-body-validator');

class UpperCase extends ImplicitRule {
passes(value) {
// return either true or false
}

getMessage() {
// return error message
}
};

Accessing Additional Data In Your Class

If your custom validation rule class needs to access all the other data undergoing validation. You can use the this.data method in the passes attribute.

passes(value) {
console.log(this.data);
}

The Trans Method

As showcased in the previous example the trans method can be used to get messages from the translation files. The :attribute placeholder will be automatically replaced by the field name.

this.trans('uppercase');

If you have a more complex scenario where you need to replace more placeholders you can pass an object as second parameter to the trans method.

this.trans('required_if_type', [
target: this.target,
type: this.type,
]);

Using Closures

You can also use closures to create custom validation rules. A closure is a function that has access to the variables in the scope in which it was created.

Here is an example of a closure-based custom validation rule:

const validator = make(data, {
title: [
'required',
'max:255',
function (value, fail, attribute) {
if (value === 'foo' {
// The trans method can also be passed to the fail method
fail(`The ${attribute} is invalid`);
});
},
],
});