It's no secret that user data can not be trusted. Therefore, one day a person came up with data validation. Well, I, for the sake of interest and benefit for, wrote my own implementation of the validator in PHP.
Kontrolio is a “regular data validation library”, designed independently of frameworks, expandable and container-friendly services. Alternatives:
Respect ,
Sirius Validation ,
Valitron and
many others .
Ideally, it is assumed that you are using some kind of service container implementation (for example,
PHP-DI ,
PHP League Container , etc.), so first you need to register Kontrolio in it:
use Kontrolio\Factory;
')
In situations where it is difficult to embed a container in a project, you can use the good old
(actually not) singleton:
use Kontrolio\Factory; $validator = Factory::getInstance() ->make($data, $rules, $messages) ->validate();
You may notice that the validation process is similar to Laravel. Indeed, I liked the way it was implemented there, so I decided to use a similar solution.
$ data ,
$ rules and
$ messages are associative arrays, where
$ data is just an array of key-value pairs (maybe multidimensional), in which the key is the name of the attribute that needs to be validated. The most interesting is in the validation rules and error messages.
Validation Rules and Error Messages
A validation rule in Kontrolio can be represented by an object of a rule class or a closure. Closures are the easiest way to describe a validation rule:
$rules = [ 'attribute' => function($value) { return $value === 'foo'; } ]; $valid = $validator ->make(['attribute' => 'bar'], $rules) ->validate(); var_dump($valid);
The closure rules, when processed by the validator, are wrapped in an object of the
Kontrolio \ Rules \ CallbackRuleWrapper class, so they have all the same options as the rule classes, and you can write a closure in this form:
'attribute' => function($value) { return [ 'valid' => $value === 'foo', 'name' => 'value_is_foo_rule', 'empty_allowed' => true, 'skip' => false, 'violations' => [] ]; }
Closures are convenient for simple rules that are not intended to be reusable. But if you plan to use the rule in different places, then it is better to write a separate rule class and then create its object:
use Kontrolio\Rules\AbstractRule; class FooRule extends AbstractRule { public function isValid($input = null) { return $input === 'foo'; } } $rules = ['attribute' => new FooRule];
Note: Kontrolio comes with many out-of-box rules.Validation Rules Options
In the rule entry in the form of closures, you notice several options that any rule supports. A little about each option below.
valid. This is a direct condition. The equivalent for the rule class is the
isValid method, which accepts one argument, the attribute value being validated. To make it clearer, I will show how you can set a validation rule for a certain attribute:
These are the easiest ways to set an attribute rule.
name. This is the name or identifier of the rule. Mainly used to generate validation error messages:
$data = ['attribute' => 'invalid']; $rules = ['attribute' => new Email]; $messages = ['attribute.email' => 'The attribute must be an email']; $validator = $container->get('validation') ->make($data, $rules, $messages); if ($validator->validate()) {
If you create a rule based on a class, you do not need to set the name / identifier of the rule manually, because by inheriting from Kontrolio \ Rules \ AbstractRule you get this default functionality in the
getName method. However, you can freely change the name of the rule simply by overriding this method.
empty_allowed. This option is useful in situations where you need to apply a validation rule only if the attribute value is present. With closures, it looks like this:
'attribute' => function($value) { return [ 'valid' => $value === 'foo', 'empty_allowed' => true ]; }
Using the class-rule with this option, you can apply it to an attribute in two ways:
In this case, the validator will respond positively if the value of the attribute is 'foo' or it is empty.
skip. This option is not similar to the previous one. It allows you to specify a condition that tells the validator to skip checking the attribute as if this rule did not exist at all.
'confirmed' => function($value) { return [ 'valid' => (bool)$value === true, 'skip' => is_admin() ]; }
The equivalent for a class rule is the
canSkipValidation method, and it works in exactly the same way:
class FooRule { public function isValid($input = null) { return (bool)$value === true; } public function canSkipValidation() { return is_admin(); } } $rules = ['confirmed' => new FooRule];
violations. I kindly borrowed this term from symfony. With the use of "violations", the user can get a more accurate error message (which you need to specify), although the validator itself, just as before, will simply return false:
$data = 'nonsense'; $rules = ['attribute' => new Email(true, true)]; $messages = [ 'attribute' => [ 'email' => "Something's wrong with your email.", 'email.mx' => 'MX record is wrong.', 'email.host' => 'Unknown email host.' ] ];
You can set as many “violations” as you wish, and each of them can then be used to describe the validation errors in more detail: from the most general message to the most detailed. See, for example, the
Kontrolio \ Rules \ Core \ Email class.
Apply several rules to attributes
Before that, all the examples showed the description of one rule to one attribute. But, of course, you can add as many rules as you like to as many attributes as you like :) Moreover, you can combine the use of closures and classes:
$rules = [ 'some' => function($value) { return $value === 'foo'; }, 'another' => [ function($value) { return $value !== 'foo'; }, new FooBarRule,
Everything is cool, of course, but there is another interesting way to write
a whole set of rules - as a string:
'attribute' => 'not_empty|length:5,15'
Here, each rule is separated by a vertical bar. I borrowed this idea from Laravel, but the difference is that any such string is “unpacked” into the usual array of rules that you have seen more than once in the article. So the line above in this case is just sugar for such an array:
'attribute' => [ new NotEmpty, new Length(5, 15) ]
Please note that everything that you write after the colon goes straight into the arguments of the constructor of the class rule:
'length:5, 15' -> new Length(5, 15)
So here we must be careful.
Skipping attribute validation entirely
Skipping a single rule or allowing empty values ​​would not be enough, so Kontrolio contains a special rule, named after Laravel -
'sometimes' and represented by the class Kontrolio \ Rules \ Core \ Sometimes. When you add this rule to an attribute, it will tell the validator to skip checking the attribute if it is not in the dataset passed to the validator, or if its value is empty. This rule should always be put first in the list.
$data = []; $rules = ['attribute' => 'sometimes|length:5,15']; $valid = $container ->get('validator') ->make($data, $rules) ->validate(); var_dump($valid);
By analogy with the previous examples, this can be written like this:
$data = []; $rules = [ 'attribute' => [ new Sometimes, new Length(5, 15) ] ]; $valid = $container ->get('validator') ->make($data, $rules) ->validate(); var_dump($valid);
Error Validation
Validation errors are stored as an associative array, where the keys are the names of the attributes, and the values ​​are the arrays with the messages themselves:
$data = ['attribute' => '']; $rules = [ 'attribute' => [ new NotBlank, new Length(5, 15) ] ]; $messages = [ 'attribute.not_blank' => 'The attr. is required.', 'attribute.length.min' => 'It must contain at lest 5 chars' ]; $valid = $container ->get('validator') ->make($data, $rules) ->validate(); $errors = $validator->getErrors();
The error dump will look like this:
[ 'attribute' => [ 0 => 'The attr. is required.', 1 => 'It must contain at lest 5 chars' ] ]
Therefore, if you just want to display all errors in a row, use the
getErrorsList validator
method . It will return a flat array with messages:
$errors = $validator->getErrorsList(); <ul class="errors"> <?php foreach($errors as $error): ?> <li class="errors__error"><?= $error; ?></li> <?php endforeach; ?> </ul>
For more complex error output, you can use the
getErrors method. It returns messages grouped by attribute name:
<ul class="errors"> <?php foreach ($errors as $attribute => $messages): <li class="errors__attribute"> <b><?= $attribute; ?></b> <ul> <?php foreach ($messages as $message): ?> <li><?= $message; ?></li> <?php endforeach; ?> </ul> </li> <?php endforeach; ?> </ul>
Concluding this story
This is how you can use
Kontrolio , another PHP data validation library. While writing this article, I thought that simply retelling the documentation would not be enough. Therefore, I plan to write an article where I will try to compare my library with other solutions.
Thanks for reading!