A re-write of rakit/validation, a standalone validation library inspired by Laravel Validation

Overview

Somnambulist Validation

GitHub Actions Build Status Issues License PHP Version Current Version

This is a re-write of rakit/validation, a standalone validator like Laravel Validation. In keeping with rakit/validation, this library does not have any other dependencies for usage.

Please note that the internal API is substantially different to rakit/validation.

Jump to rules

Requirements

  • PHP 8.0+
  • ext/mb-string

Installation

Install using composer, or checkout / pull the files from github.com.

  • composer require somnambulist/validation

Usage

There are two ways for validating data with this library: using make to make a validation object, then validate it using validate; or use validate.

For example:

Using make:

<?php

require('vendor/autoload.php');

use Somnambulist\Components\Validation\Factory;

$validation = (new Factory)->make($_POST + $_FILES, [
    'name'                  => 'required',
    'email'                 => 'required|email',
    'password'              => 'required|min:6',
    'confirm_password'      => 'required|same:password',
    'avatar'                => 'required|uploaded_file:0,500K,png,jpeg',
    'skills'                => 'array',
    'skills.*.id'           => 'required|numeric',
    'skills.*.percentage'   => 'required|numeric'
]);
$validation->validate();

if ($validation->fails()) {
    // handling errors
    $errors = $validation->errors();
    echo "<pre>";
    print_r($errors->firstOfAll());
    echo "</pre>";
    exit;
} else {
    // validation passes
    echo "Success!";
}

or via validate:

<?php

require('vendor/autoload.php');

use Somnambulist\Components\Validation\Factory;

$validation = (new Factory)->validate($_POST + $_FILES, [
    'name'                  => 'required',
    'email'                 => 'required|email',
    'password'              => 'required|min:6',
    'confirm_password'      => 'required|same:password',
    'avatar'                => 'required|uploaded_file:0,500K,png,jpeg',
    'skills'                => 'array',
    'skills.*.id'           => 'required|numeric',
    'skills.*.percentage'   => 'required|numeric'
]);

if ($validation->fails()) {
	// handling errors
	$errors = $validation->errors();
	echo "<pre>";
	print_r($errors->firstOfAll());
	echo "</pre>";
	exit;
} else {
	// validation passes
	echo "Success!";
}

You are strongly advised to use a Dependency Injection container and store the Factory as a singleton instead of creating new instances. This will reduce the penalty for creating validation instances and allow custom rules to be more easily managed.

Attribute Aliases

Unlike rakit/validation, attribute names are not transformed in any way; instead, if you wish to name your attributes, aliases must be used.

Aliases can be defined in several ways: on the rule itself, or by adding the alias to the validation. Note that aliases should be set before calling validate.

use Somnambulist\Components\Validation\Factory;

$validation = (new Factory)->make([
	'province_id' => $_POST['province_id'],
	'district_id' => $_POST['district_id']
], [
	'province_id:Province' => 'required|numeric',
	'district_id:District' => 'required|numeric'
]);

// or set the aliases:
$validation->setAlias('province_id', 'Province');
$validation->setAlias('district_id', 'District');

// then validate it
$validation->validate();

Validation Messages

Validation messages are defined in Resources/i18n/en.php. Any message can be replaced with a custom string, or translated to another language. The English strings are always loaded during Factory instantiation.

Depending on the failure type, various variables will be available to use, however, the following are always available for all messages:

  • :attribute: the attribute under validation, alias will be used if set,
  • :value: the value of the attribute under validation, converted to string with arrays and objects as JSON strings.

Custom Messages for Validator

All messages are stored in a MessageBag on the Factory instance. Additional languages can be added to this message bag, or customised on the specific validation instance. Additionally, the default language can be set on the message bag on the Factory, or a specific language set on the validation instance.

To add a new set of messages:

use Somnambulist\Components\Validation\Factory;

$factory = new Factory();
$factory->messages()->add('es', [
    'rule.required' => 'Se requiere :attribute',
]);

$validation = $factory->validate($inputs, $rules);
$validation->setLanguage('es')->validate();

Or override the default English strings:

use Somnambulist\Components\Validation\Factory;

$factory = new Factory();
$factory->messages()->replace('en', 'rule.required', 'Se requiere :attribute');

$validation = $factory->validate($inputs, $rules);
$validation->validate();

Or set the default language:

use Somnambulist\Components\Validation\Factory;

$factory = new Factory();
$factory->messages()->default('es');

$validation = $factory->validate($inputs, $rules);
$validation->validate();

Custom Message for Specific Attribute Rule

Sometimes you may want to set custom messages for specific attribute rules to make them more explicit or to add other information. This is done by adding a message key for the attribute with a : and the rule name.

For example:

use Somnambulist\Components\Validation\Factory;

$validator = new Factory();
$validation_a = $validator->make($input, [
	'age' => 'required|min:18'
]);

$validation->messages()->add('en', 'age:min', '18+ only');

$validation->validate();

Custom Messages for Rules

Some rules have several possible validation messages. These are all named as rule.<name>.<check>. To change the message, override or add the specific message.

For example, uploaded_file can have failures for the file, min/max size and type. These are bound to:

  • rule.uploaded_file
  • rule.uploaded_file.min_size
  • rule.uploaded_file.max_size
  • rule.uploaded_file.type

To change any of the sub-messages, add/override that message key on the message bag.

For example:

use Somnambulist\Components\Validation\Factory;

$validator = new Factory();
$validation_a = $validator->make($input, [
	'age' => 'required|min:18'
]);

$validation->messages()->add('en', 'age:min', '18+ only');

$validation->validate();

Unlike rakit, it is not possible to set custom messages in the Rule instances directly. Any message must be set in the message bag.

Complex Translation Needs

The system for translations in this library is rather basic. If you have complex needs, or wish to handle countables etc. Then all error messages are stored as ErrorMessage instances containing the message key and the variables for that message.

Instead of using the ErrorBag to display messages, you can use the underlying array (or a DataBag instance) and then pass the message keys to your translation system along with the variables.

Note that errors are a nested set by attribute and rule name.

Working with Error Messages

Error messages are collected in an ErrorBag instance that you can access via errors() on the validation instance.

use Somnambulist\Components\Validation\Factory;

$validation = (new Factory())->validate($inputs, $rules);

$errors = $validation->errors();

Now you can use the following methods to retrieve the messages:

all(string $format = ':message')

Get all messages as a flattened array:

$messages = $errors->all();
// [
//     'email is not a valid email address',
//     'password minimum is 6 characters',
//     'password must contain capital letters'
// ]

$messages = $errors->all('<li>:message</li>');
// [
//     '<li>email is not a valid email address</li>',
//     '<li>password minimum is 6 character</li>',
//     '<li>password must contain capital letters</li>'
// ]

firstOfAll(string $format = ':message', bool $dotNotation = false)

Get only the first message from all existing keys:

$messages = $errors->firstOfAll();
// [
//     'email' => 'Email is not valid email',
//     'password' => 'Password minimum 6 character',
// ]

$messages = $errors->firstOfAll('<li>:message</li>');
// [
//     'email' => '<li>Email is not valid email</li>',
//     'password' => '<li>Password minimum 6 character</li>',
// ]

Argument $dotNotation is for array validation. If it is false it will return the original array structure, if it is true it will return a flattened array with dot notation keys.

For example:

$messages = $errors->firstOfAll(':message', false);
// [
//     'contacts' => [
//          1 => [
//              'email' => 'Email is not valid email',
//              'phone' => 'Phone is not valid phone number'
//          ],
//     ],
// ]

$messages = $errors->firstOfAll(':message', true);
// [
//     'contacts.1.email' => 'Email is not valid email',
//     'contacts.1.phone' => 'Email is not valid phone number',
// ]

first(string $key)

Get the first message for the given key. It will return a string if key has any error message, or null if the key has no errors.

For example:

if ($emailError = $errors->first('email')) {
    echo $emailError;
}

toArray()

Get the raw underlying associative array of ErrorMessage objects.

For example:

$messages = $errors->toArray();
// [
//     'email' => [
//         'email' => 'Email is not valid email'
//     ],
//     'password' => [
//         'min' => 'Password minimum 6 character',
//         'regex' => Password must contains capital letters'
//     ]
// ]

toDataBag()

Get the raw underlying associative array of ErrorMessage objects as a DataBag instance.

For example:

$message = $errors->toDataBag()->filter()->first();

count()

Get the number of error messages.

has(string $key)

Check if the given key has an error. It returns true if a key has an error, and false otherwise.

Validated, Valid, and Invalid Data

After validation, the data results are held in each validation instance. For example:

use Somnambulist\Components\Validation\Factory;

$validation = (new Factory())->validate([
    'title' => 'Lorem Ipsum',
    'body' => 'Lorem ipsum dolor sit amet ...',
    'published' => null,
    'something' => '-invalid-'
], [
    'title' => 'required',
    'body' => 'required',
    'published' => 'default:1|required|in:0,1',
    'something' => 'required|numeric'
]);

Now you can get the validated data, only the valid data, or only the invalid data:

$validatedData = $validation->getValidatedData();
// [
//     'title' => 'Lorem Ipsum',
//     'body' => 'Lorem ipsum dolor sit amet ...',
//     'published' => '1' // notice this
//     'something' => '-invalid-'
// ]

$validData = $validation->getValidData();
// [
//     'title' => 'Lorem Ipsum',
//     'body' => 'Lorem ipsum dolor sit amet ...',
//     'published' => '1'
// ]

$invalidData = $validation->getInvalidData();
// [
//     'something' => '-invalid-'
// ]

Available Rules

Click to show details.

accepted

The field under this rule must be one of 'on', 'yes', '1', 'true' (the string "true"), or true.

after:tomorrow

The field under this rule must be a date after the given minimum.

The parameter should be any valid string that can be parsed by strtotime. For example:

  • after:next week
  • after:2016-12-31
  • after:2016
  • after:2016-12-31 09:56:02
alpha

The field under this rule must be entirely alphabetic characters.

alpha_num

The field under this rule must be entirely alpha-numeric characters.

alpha_dash

The field under this rule may have alpha-numeric characters, as well as dashes and underscores.

alpha_spaces

The field under this rule may have alpha characters, as well as spaces.

any_of:value,value,value

A variation of in: here the values (separated by default with a ,) must all be in the given values. For example: order => 'name,date' with the rule any_of:name,id would fail validation as date is not part of the allowed values. The separator can be changed by calling separator() on the rule instance.

use Somnambulist\Components\Validation\Factory;
use Somnambulist\Components\Validation\Rules\AnyOf;

$validation = $factory->validate([
    'field' => 'foo;bar;example'
], [
    'field' => $factory->rule('any_of')->separator(';')->values(['foo', 'bar']),
]);

$validation->passes(); // true if field only contains the values in any_of

Like in, comparisons can be performed use strict matching by calling ->strict(true) on the rule.

This rule is useful for APIs that allow comma separated data as a single parameter e.g. JsonAPI include, order etc. If the source is already an array, then array|in:... can be used instead.

array

The field under this rule must be an array.

before:yesterday

The field under this rule must be a date before the given maximum.

This also works the same way as the after rule. Pass anything that can be parsed by strtotime

between:min,max

The field under this rule must have a size between min and max params. Value size is calculated in the same way as min and max rule.

You can also validate the size of uploaded files using this rule:

$validation = $validator->validate([
    'photo' => $_FILES['photo']
], [
    'photo' => 'required|between:1M,2M'
]);
boolean

The field under this rule must be boolean. Accepted inputs are true, false, 1, 0, "1", and "0".

callback

Define a custom callback to validate the value. This rule cannot be registered using the string syntax. To use this rule, you must use the array syntax and either explicitly specify callback, or pass the closure:

$validation = $validator->validate($_POST, [
    'even_number' => [
        'required',
        function ($value) {
            // false = invalid
            return (is_numeric($value) AND $value % 2 === 0);
        },
        'callback' => fn ($v) => is_numeric($v) && $v % 2 === 0,
    ]
]);

You can set a custom message by returning a string instead of false:

$validation = $validator->validate($_POST, [
    'even_number' => [
        'required',
        function ($value) {
            if (!is_numeric($value)) {
                return ":attribute must be numeric.";
            }
            if ($value % 2 !== 0) {
                return ":attribute is not even number.";
            }
            
            return true; // always return true if validation passes
        }
    ]
]);

Note: callback closures are bound to the rule instance allowing access to rule properties via $this.

date:format

The field under this rule must be valid date following a given format. Parameter format is optional, default format is Y-m-d.

default/defaults

If the attribute has no value, this default will be used in place in the validated data.

For example if you have validation like this

use Somnambulist\Components\Validation\Factory;

$validation = (new Factory)->validate([
    'enabled' => null
], [
    'enabled' => 'default:1|required|in:0,1'
    'published' => 'default:0|required|in:0,1'
]);

$validation->passes(); // true

// Get the valid/default data
$valid_data = $validation->getValidData();

$enabled = $valid_data['enabled'];
$published = $valid_data['published'];

Validation passes because the default value for enabled and published is set to 1 and 0 which is valid.

different:another_field

Opposite of same; the field value under this rule must be different to another_field value.

digits:value

The field under validation must be numeric and must have an exact length of value.

digits_between:min,max

The field under validation must be numeric and have a length between the given min and max.

email

The field under this validation must be a valid email address according to the built-in PHP filter extension.

See FILTER_VALIDATE_EMAIL for details.

exists:table,column (database)

The field under this validation must exist in the given table. This does not check for uniqueness, only that at least one record for the provided value and column in the table is there.

To use this rule, you must provide a DBAL connection. This should be done via dependency injection.

For example:

use Somnambulist\Components\Validation\Factory;

$validation = (new Factory)->validate([
    'country' => 'GBR'
], [
    'country' => 'exists:countries,id',
]);

$validation->passes(); // true if table countries has a record with id GBR

For more refined validation, the underlying query may be modified by setting a closure by calling ->where(). The closure will be passed a Doctrine\DBAL\Query\QueryBuilder instance.

use Doctrine\DBAL\Query\QueryBuilder;
use Somnambulist\Components\Validation\Factory;
use Somnambulist\Components\Validation\Rules\Exists;

$factory    = new Factory;
$factory->addRule('exists', new Exists($dbalConn));

$validation = $factory->validate([
    'country' => 'GBR'
], [
    'country' => $factory->rule('exists')->table('countries')->column('id')->where(fn (QueryBuilder $qb) => $qb->andWhere('active = 1')),
]);

$validation->passes(); // true if table countries has a record with id GBR and it is active
extension:extension_a,extension_b,...

The field under this rule must end with an extension corresponding to one of those listed.

This is useful for validating a file type for a given path or url. The mimes rule should be used for validating uploads.

If you require strict mime checking you should implement a custom MimeTypeGuesser that can make use of a server side file checker that uses a mime library.

float

The field under this rule must be a floating point number, for example: 0.0 12.3456 etc. The value may be a string containing a float. Note that integers and 0 (zero) will fail validation with this rule.

in:value_1,value_2,...

The field under this rule must be included in the given list of values.

To help build the string rule, the In (and NotIn) rules have a helper method:

use Somnambulist\Components\Validation\Factory;
use Somnambulist\Components\Validation\Rules\In;

$factory = new Factory();
$validation = $factory->validate($data, [
    'enabled' => [
        'required',
        In::make([true, 1])
    ]
]);

This rule uses in_array to perform the validation and by default does not perform strict checking. If you require strict checking, you can invoke the rule like this:

use Somnambulist\Components\Validation\Factory;

$factory = new Factory();
$validation = $factory->validate($data, [
    'enabled' => [
        'required',
        $factory->rule('in')->values([true, 1])->strict()
    ]
]);

Then 'enabled' value should be boolean true, or int 1.

integer

The field under validation must be an integer.

ip

The field under this rule must be a valid ipv4 or ipv6 address.

ipv4

The field under this rule must be a valid ipv4 address.

ipv6

The field under this rule must be a valid ipv6 address.

json

The field under this validation must be a valid JSON string.

length:number

The field under this validation must be a string of exactly the length specified.

lowercase

The field under this validation must be in lowercase.

max:number

The field under this rule must have a size less than or equal to the given number. Value size is calculated in the same way as the min rule.

You can also validate the maximum size of uploaded files using this rule:

$validation = $validator->validate([
    'photo' => $_FILES['photo']
], [
    'photo' => 'required|max:2M'
]);
mimes:extension_a,extension_b,...

The $_FILES item under validation must have a MIME type corresponding to one of the listed extensions.

This works on file extension and not client sent headers or embedded file type. If you require strict mime type validation you are recommended to implement a custom MimeTypeGuesser that uses a full mime-type lookup library and replace the built-in mime rule.

Additional mime types can be added to the existing guesser by using dependency injection and keeping the mime type guesser as a service.

min:number

The field under this rule must have a size greater than or equal to the given number.

For string values, the size corresponds to the number of characters. For integer or float values, size corresponds to its numerical value. For an array, size corresponds to the count of the array. If your value is numeric string, you can use the numeric rule to treat its size as a numeric value instead of the number of characters.

You can also validate the minimum size of uploaded files using this rule:

$validation = $validator->validate([
    'photo' => $_FILES['photo']
], [
    'photo' => 'required|min:1M'
]);
not_in:value_1,value_2,...

The field under this rule must not be included in the given list of values.

This rule also uses in_array and can have strict checks enabled the same way as In.

nullable

The field under this rule may be empty.

numeric

The field under this rule must be numeric.

present

The field under this rule must be in the set of inputs, whatever the value is.

prohibited

The field under this rule is not allowed.

prohibited_if

The field under this rule is not allowed if another_field is provided with any of the value(s).

prohibited_unless

The field under this rule is not allowed unless another_field has one of these values. This is the inverse of prohibited_if.

regex:/your-regex/

The field under this rule must match the given regex. Note: if you require the use of |, then the regex rule must be written in array format instead of as a string. For example:

use Somnambulist\Components\Validation\Factory;

$validation = (new Factory())->validate([
    'field' => 'value'
], [
    'field' => [
        'required',
        'regex' => '/(this|that|value)/'
    ]
])
rejected

The field under this rule must have a value that corresponds to rejection i.e. 0 (zero), "0", false, no, "false", off. This is the inverse of the accepted rule.

required

The field under this validation must be present and not 'empty'.

Here are some examples:

Value Valid
'something' true
'0' true
0 true
[0] true
[null] true
null false
[] false
'' false

For uploaded files, $_FILES['key']['error'] must not be UPLOAD_ERR_NO_FILE.

required_if:another_field,value_1,value_2,...

The field under this rule must be present and not empty if the another_field field is equal to any value.

For example required_if:something,1,yes,on will be required if something's value is one of 1, '1', 'yes', or 'on'.

required_unless:another_field,value_1,value_2,...

The field under validation must be present and not empty unless the another_field field is equal to any value.

required_with:field_1,field_2,...

The field under validation must be present and not empty only if any of the other specified fields are present.

required_without:field_1,field_2,...

The field under validation must be present and not empty only when any of the other specified fields are not present.

required_with_all:field_1,field_2,...

The field under validation must be present and not empty only if all of the other specified fields are present.

required_without_all:field_1,field_2,...

The field under validation must be present and not empty only when all of the other specified fields are not present.

same:another_field

The field value under this rule must have the same value as another_field.

sometimes

The field should only be validated if present in the input data. For example: field => sometimes|required|email

string

The field under this rule must be a PHP string.

unique:table,column,ignore,ignore_column (database)

The field under this validation must be unique in the given table. Optionally: a value may be ignored and this could be an alternative column value if the ignore_column is given.

To use this rule, you must provide a DBAL connection. This should be done via dependency injection.

For example:

use Somnambulist\Components\Validation\Factory;

$validation = (new Factory)->validate([
    'email' => '[email protected]'
], [
    'email' => 'email|unique:users,email',
]);

$validation->passes(); // true if table users does not contain the email

Ignore the current users email address:

use Somnambulist\Components\Validation\Factory;

$validation = (new Factory)->validate([
    'email' => '[email protected]'
], [
    'email' => 'email|unique:users,email,10,id',
]);

$validation->passes(); // true if table users ignoring id 10, does not contain email

For more refined validation, the underlying query may be modified by setting a closure by calling ->where(). The closure will be passed a Doctrine\DBAL\Query\QueryBuilder instance.

use Doctrine\DBAL\Query\QueryBuilder;
use Somnambulist\Components\Validation\Factory;
use Somnambulist\Components\Validation\Rules\Unique;

$factory    = new Factory;
$factory->addRule('unique', new Unique($dbalConn));

$validation = $factory->validate([
    'email' => '[email protected]'
], [
    'email' => $factory->rule('unique')->table('users')->column('email')->where(fn (QueryBuilder $qb) => $qb->andWhere('active = 1')),
]);

$validation->passes(); // true if table users does not contain an active email
uploaded_file:min_size,max_size,extension_a,extension_b,...

This rule will validate data from $_FILES. The field under this rule has the following conditions:

  • $_FILES['key']['error'] must be UPLOAD_ERR_OK or UPLOAD_ERR_NO_FILE. For UPLOAD_ERR_NO_FILE you can validate it with required rule.
  • If min size is given, uploaded file size MUST NOT be lower than min size.
  • If max size is given, uploaded file size MUST NOT be higher than max size.
  • If file types is given, mime type must be one of those given types.

For size constraints both must be given when using the string definition. To specify only a max size, use the factory to fetch the rule and use method chaining.

Here are some example definitions and explanations:

  • uploaded_file: uploaded file is optional. When it is not empty, it must be ERR_UPLOAD_OK.
  • required|uploaded_file: uploaded file is required, and it must be ERR_UPLOAD_OK.
  • uploaded_file:0,1M: uploaded file size must be between 0 - 1 MB, but uploaded file is optional.
  • required|uploaded_file:0,1M,png,jpeg: uploaded file size must be between 0 - 1MB and mime types must be image/jpeg or image/png.

For multiple file uploads, PHP uses the format _FILES[key][name][0..n+1] (see PHP manual for more details). Instead the files array is automatically re-ordered to a nested array of related attributes. This allows multiple files to be validated using the same rule.

For example if you have input files like this:

<input type="file" name="photos[]"/>
<input type="file" name="photos[]"/>
<input type="file" name="photos[]"/>

You can validate all the files by using:

$validation = (new Factory)->validate($_FILES, [
    'photos.*' => 'uploaded_file:0,2M,jpeg,png'
]);

// or

$validation = (new Factory)->validate($_FILES, [
    'photos.*' => 'uploaded_file|max:2M|mimes:jpeg,png'
]);

Or if you have input files like this:

<input type="file" name="images[profile]"/>
<input type="file" name="images[cover]"/>

You can validate it like this:

$validation = (new Factory)->validate($_FILES, [
    'images.*' => 'uploaded_file|max:2M|mimes:jpeg,png',
]);

// or

$validation = (new Factory)->validate($_FILES, [
    'images.profile' => 'uploaded_file|max:2M|mimes:jpeg,png',
    'images.cover' => 'uploaded_file|max:5M|mimes:jpeg,png',
]);
uppercase

The field under this validation must be in uppercase.

url

The field under this rule must be a valid url format. The default is to validate the common format: any_scheme://.... You can specify specific URL schemes if you wish.

For example:

$validation = (new Factory)->validate($inputs, [
    'random_url' => 'url',          // value can be `any_scheme://...`
    'https_url' => 'url:http',      // value must be started with `https://`
    'http_url' => 'url:http,https', // value must be started with `http://` or `https://`
    'ftp_url' => 'url:ftp',         // value must be started with `ftp://`
    'custom_url' => 'url:custom',   // value must be started with `custom://`
]);

Unlike rakit, mailto and JDBC are not supported. Implement a custom rule or a regex to validate these.

uuid

The field under this validation must be a valid UUID and not the nil UUID string.

Register/Override Rules

By default, all built-in rules are registered automatically to the Factory instance. Some of these are required internally (e.g. required and callback); however you can override or add any number of new rules to the factory to use for your validations.

This is done by accessing the addRule() method on the Factory and adding a new rule instance.

For example, you want to create the unique validator that will check field availability in a database.

First, lets create UniqueRule class:

<?php declare(strict_types=1);

use Somnambulist\Components\Validation\Rule;

class UniqueRule extends Rule
{
    protected string $message = ":attribute :value has been used";
    protected array $fillableParams = ['table', 'column', 'except'];
    protected PDO $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function check($value): bool
    {
        // make sure required parameters exists
        $this->assertHasRequiredParameters(['table', 'column']);

        // getting parameters
        $column = $this->parameter('column');
        $table = $this->parameter('table');
        $except = $this->parameter('except');

        if ($except && $except == $value) {
            return true;
        }

        // do query
        $stmt = $this->pdo->prepare(sprintf('select count(*) as count from %s where %s = :value', $table, $column));
        $stmt->bindParam(':value', $value);
        $stmt->execute();
        $data = $stmt->fetch(PDO::FETCH_ASSOC);

        // true for valid, false for invalid
        return intval($data['count']) === 0;
    }
}

Now to register this rule it needs adding to the Factory instance:

use Somnambulist\Components\Validation\Factory;

$factory = new Factory();
$factory->addRule('unique', new UniqueRule($pdo));

Now you can use it like this:

$validation = $factory->validate($_POST, [
    'email' => 'email|unique:users,email,[email protected]'
]);

In the UniqueRule above, the property $message is used for the invalid message. The property $fillableParams defines the order and names of the arguments for the rule. By default, fillParameters will fill parameters listed in $fillableParams from the string rules. For example, unique:users,email,[email protected] in example above, will set:

$params['table'] = 'users';
$params['column'] = 'email';
$params['except'] = '[email protected]';

If you want your custom rule to accept parameter lists like in,not_in, or uploaded_file rules, you need to override the fillParameters(array $params) method in your custom rule class.

Note that the unique rule that we created above also can be used like this:

$validation = $factory->validate($_POST, [
    'email' => [
    	'required', 'email',
    	$factory('unique', 'users', 'email')
    ]
]);

You can improve UniqueRule class above by adding some methods to set the params instead of using the string format:

<?php

class UniqueRule extends Rule
{
    public function table(string $table): self
    {
        $this->params['table'] = $table;
        
        return $this;
    }

    public function column(string $column): self
    {
        $this->params['column'] = $column;
        
        return $this;
    }

    public function except(string $value): self
    {
        $this->params['except'] = $value;
        
        return $this;
    }
}

Now configuring the rule becomes:

$validation = $factory->validate($_POST, [
    'email' => [
    	'required', 'email',
    	$validator('unique')->table('users')->column('email')->except('[email protected]')
    ]
]);

Implicit Rule

An implicit rule is a rule that if it's invalid, the next rules will be ignored. For example, if the attribute didn't pass required* rules, the next rules will be invalid. To prevent unnecessary validation and error messages, we make required* rules to be implicit.

To make your custom rule implicit, you can make $implicit property value to be true. For example:

<?php
use Somnambulist\Components\Validation\Rule;

class YourCustomRule extends Rule
{
    protected bool $implicit = true;
}

Modify Value

In some cases, you may want your custom rule to be able to modify the attribute value like the default/defaults rule. In the current and next rule checks, your modified value will be used.

To do this, you should implement Somnambulist\Components\Validation\Rules\Contracts\ModifyValue and create the method modifyValue(mixed $value) on your custom rule class.

For example:

<?php

use Somnambulist\Components\Validation\Rule;
use Somnambulist\Components\Validation\Rules\Contracts\ModifyValue;

class YourCustomRule extends Rule implements ModifyValue
{
    public function modifyValue(mixed $value): mixed
    {
        // Do something with $value

        return $value;
    }
}

Before Validation Hook

You may want to do some preparation before running the validation. For example, the uploaded_file rule will resolve the attribute value that comes from $_FILES (undesirable) array structure to be a well-organized array.

To do this, you should implement Somnambulist\Components\Validation\Rules\Contracts\BeforeValidate and create the method beforeValidate() on your custom rule class.

For example:

<?php

use Somnambulist\Components\Validation\Rule;
use Somnambulist\Components\Validation\Rules\Contracts\BeforeValidate;

class YourCustomRule extends Rule implements BeforeValidate
{
    public function beforeValidate(): void
    {
        $attribute = $this->getAttribute();
        $validation = $this->validation;

        // Do something with $attribute and $validation
        // For example change attribute value
        $validation->setValue($attribute->getKey(), "your custom value");
    }
}

Tests

PHPUnit 9+ is used for testing. Run tests via vendor/bin/phpunit.

Comments
  • TypeError is thrown if rules like Url,Regex recieves default NULL value

    TypeError is thrown if rules like Url,Regex recieves default NULL value

    Somnambulist\Components\Validation\Rules\Url::validateCommonScheme(): Argument #1 ($value) must be of type string, null given, called in .../vendor/somnambulist/validation/src/Rules/Url.php on line 32

    So it's impossible to use them as is in cases when validated values are optional

    opened by jagdtiger9 5
  • Patch 2

    Patch 2

    With this PR i make it possible to change the default separator which used when rule has multiple attributes. For some rules, it is required to use the : in die rule and not as separator.

    After merge this PR, the Factory get a new variable, which define the current separator.

    Kind regards

    opened by xJuvi 4
  • Uncaught TypeError: is_uploaded_file() must be of type string

    Uncaught TypeError: is_uploaded_file() must be of type string

    When using a file input with the 'multiple' attribute, you must add a validation rule with the dot syntax e.g. photos.* or it throws an error if one or more files is attached:

    Uncaught TypeError: is_uploaded_file(): Argument #1 ($filename) must be of type string, array given in .../somnambulist/validation/src/Rules/Behaviours/CanValidateFiles.php:11
    

    I assume this is expected behavior, but the uncaught error should probably return an exception.

    In addition, when requiring a file to be attached to a multiple-file input, it does not throw a validation error if no file is attached: <input type="file" name="photos" multiple accept=".jpg,.jpeg" />

    // expected behavior is to throw validation error when no file is attached:
    $validation = [
      'photos.*' => 'required|uploaded_file:0,1M,jpeg',
    ];
    

    Am I interpreting or writing the rule wrong, or am I correct in that this is a bug?

    bug 
    opened by danFWD 3
  • Regex pipe validation error

    Regex pipe validation error

    Hi,

    a pipe as or in a regular expression is recognized as a new validation rule. I have replaced the preg_split in resolveRules. Maybe there is a nicer solution.

            if (is_string($rules)) {
                preg_match('/(?=[^|])(?:[^|]*\/[^)]+\/)*[^|]*/', $rules, $rules) ;
            }
    

    Eric

    enhancement question 
    opened by McHill007 3
  • alpha_num ruleTypeError: preg_match(): Argument #2 ($subject) must be of type string, int given

    alpha_num ruleTypeError: preg_match(): Argument #2 ($subject) must be of type string, int given

        public function check(mixed $value): bool
        {
            if (!is_string($value) && !is_numeric($value)) {
                return false;
            }
    
            return preg_match('/^[\pL\pM\pN]+$/u', $value) > 0;
        }
    

    Kind of logical error here If statement allowes string&integer types but preg_match 2-nd param is a string only

    bug 
    opened by jagdtiger9 1
  • starts_with & ends_with rules

    starts_with & ends_with rules

    Hey,

    I created starts_with & ends_with rules. It works with stings, arrays and numbers.

    Maybe this would be interesting to be a general rule.

    ~ Marius

    opened by DeveloperMarius 1
  • Minor `README.md` changes

    Minor `README.md` changes

    I noticed a couple of things that could be tweaked in the readme file. Very minor changes.

    • Minor grammar changes
    • Add in a missing single quotation mark.

    P.S. love the library, thanks for your work! I hope to contribute more in the future 😊

    opened by AshleyField 1
  • Add PHP7.4 support

    Add PHP7.4 support

    Hello, is it possible for PHP7.4 support to be added... it's an awesome library, and i have a legacy code which still uses php7.4 and i'd really love if we had a 7.4 support

    question 
    opened by Ghostscypher 1
  • phpstan & Github actions

    phpstan & Github actions

    The readme says it works with PHP 8.0, actually there was a problem with the used phpunit version that I fixed by setting a proper version constraint (the notice was fixed in 9.5.5). composer update --prefer-lowest --prefer-stable revealed that.

    I've also made all private calls protected, IMHO methods should never be private in a library.

    GH actions will now run for 8.0 and 8.1.

    And I renamed the bags to collection. Sorry, but "bag" hurts me. 😄 Feel free to take the change or change it back, but I wanted to share my changes.

    opened by floriankraemer 1
Owner
Somnambulist Tech
Providing open source packages to help with web application development
Somnambulist Tech
Lightweight and feature-rich PHP validation and filtering library. Support scene grouping, pre-filtering, array checking, custom validators, custom messages. 轻量且功能丰富的PHP验证、过滤库。支持场景分组,前置过滤,数组检查,自定义验证器,自定义消息。

PHP Validate 一个简洁小巧且功能完善的php验证、过滤库。 简单方便,支持添加自定义验证器 支持前置验证检查, 自定义如何判断非空 支持将规则按场景进行分组设置。或者部分验证 支持在进行验证前对值使用过滤器进行净化过滤内置过滤器 支持在进行验证前置处理和后置处理独立验证处理 支持自定义每

Inhere 246 Jan 5, 2023
[READ-ONLY] Validation library from CakePHP. This repo is a split of the main code that can be found in https://github.com/cakephp/cakephp

CakePHP Validation Library The validation library in CakePHP provides features to build validators that can validate arbitrary arrays of data with eas

CakePHP 39 Oct 11, 2022
Light and extendable schema validation library

Light PHP validation library For everyone who uses MongoDB or other NoSQL solution and cares about what client sends to his/her database and looking f

Alexander Serkin 43 Sep 28, 2022
An extensible validation library for your data with sane defaults.

Hird Hirds, also known as housecarls, was a gathering of hirdmen, who functioned as the king's personal guards during the viking age and the early mid

Asko Nõmm 13 Apr 23, 2022
FyreValidation is a free, open-source validation library for PHP.

FyreValidation FyreValidation is a free, validation library for PHP. Table Of Contents Installation Validators Rules Error Messages Installation Using

Elusive 0 Jan 15, 2022
🔒 Laravel validation rule that checks if a password has been exposed in a data breach.

?? Laravel Password Exposed Validation Rule This package provides a Laravel validation rule that checks if a password has been exposed in a data breac

Jordan Hall 85 Apr 26, 2022
高性能的验证器组件(Validation),适用于 Hyperf 或 Laravel 框架,可获得数百倍的性能提升

验证器 简介 兼容 Hyperf/Laravel Validation 规则 部分场景可获得约 500 倍性能提升 验证器可多次复用不同数据,无状态设计 规则可全局复用 智能合并验证规则 安装 环境要求 PHP >= 8.0 mbstring 扩展 ctype 扩展 安装命令 composer re

KK集团 80 Dec 9, 2022
Extension for the Laravel validation class

Intervention Validation Intervention Validation is an extension library for Laravel's own validation system. The package adds rules to validate data l

null 370 Dec 30, 2022
Extra validation rules for dealing with images in Laravel 5.

Image-Validator Extra validation rules for dealing with images in Laravel 5. NOTE: As of Laravel version 5.2, there are now built-in validation rules

Colin Viebrock 223 Jun 16, 2022
Laravel Validation Service

Laravel Validation Service Installation Add "prettus/laravel-repository": "1.1.*" to composer.json "prettus/laravel-validation": "1.1.*" Create a vali

Anderson Andrade 398 Nov 25, 2022
🔒 Laravel validation rule that checks if a password has been exposed in a data breach.

?? Laravel Password Exposed Validation Rule This package provides a Laravel validation rule that checks if a password has been exposed in a data breac

Jordan Hall 85 Apr 26, 2022
File uploads with validation and storage strategies

Upload This component simplifies file validation and uploading. Usage Assume a file is uploaded with this HTML form: <form method="POST" enctype="mult

Brandon Savage 1.7k Dec 27, 2022
Abstracts HTTP request input handling, providing an easy interface for data hydration and validation

Linio Input Linio Input is yet another component of the Linio Framework. It aims to abstract HTTP request input handling, allowing a seamless integrat

Linio 41 Dec 12, 2021
One Line Validation, For CodeIgniter 4

One Line Validation (OLV) is a package made for CodeIgniter 4 to provide a fast and single line validation experience ideal for CDN and/or API services consuming the Validation System from CodeIgniter 4.

AJ Meireles 2 Sep 21, 2021
Validation rules for Money and Currency

money-validation-laravel Validation rules for Money and Currency Installation composer require brokeyourbike/money-validation-laravel Usage Package us

Ivan Stasiuk 1 Oct 25, 2021
laminas-password-validator provides a validator for character-set based input validation.

laminas-password-validator laminas-password-validator provides a validator for character-set based input validation. Installation composer require pra

null 1 Mar 8, 2022
PHP library - Validators for standards from ISO, International Finance, Public Administrations, GS1, Manufacturing Industry, Phone numbers & Zipcodes for many countries

IsoCodes PHP library - Validators for standards from ISO, International Finance, Public Administrations, GS1, Book Industry, Phone numbers & Zipcodes

Ronan Guilloux 767 Jan 2, 2023
A simple filtering library for PHP

Filterus - A flexible PHP 5.3 filter package Filter Methods: Each filter class has two primary methods: $filter->filter($var) - returns a modified ver

Anthony Ferrara 451 Dec 27, 2022
Library that offers Input Filtering based on Annotations for use with Objects. Check out 2.dev for 2.0 pre-release.

DMS Filter Component This library provides a service that can be used to filter object values based on annotations Install Use composer to add DMS\Fil

Rafael Dohms 89 Nov 28, 2022