xtompie/validation

Validation component


Keywords
validation, input
License
MIT

Documentation

Validation

Validation component to validate models, input data. Handle any data types - arrays, objects, scalars, getter methods. Easy to extend. Type hinting / autocompletion. Fluent syntax.

use Xtompie\Validation\Validation;

$result = Validation::of($input)
    ->key('email')->required()->email()
    ->key('password')->required()->min(3)
    ->group()
    ->main('password')->callback(fn($input) => $input['email'] != $input['password'])
    ->group()
    ->key('email')->callback(fn($email) => !inUse($email))
    ->result();

$result is Xtompie\Result\Result

Requiments

PHP >= 8.0

Installation

Using composer

composer require xtompie/validation

Docs

Subject

Validation subject can be provided by

Validation::of($input);
Validation::new()->withSubject($input);
Validation::new()->validate($input);

Groups

Validation::of($input)
    /* Group 1 */
    ->group()
    /* Group 2 */
    ->group()
    /* Group 3 */
    ;

If an error occurs during group validation, subsequent groups will not be validated and validation will stop.

Targets

Validation::new()
    ->main() // validation will target main subject
    ->property($name) // when subject is an object, will target property named $name
    ->method($name) // when subject is an object, will target getter method named $name
    ->key($key) // when subject is an array, will target array value where key is $key
    ->take($callback) // custom target $callback, as first argument main subject will be given
;

Nested Target

Targets can be nested e.g.

    $validation = Validation::of(['person' => ['name' => 'John']])
        ->key('person')
        ->nested()->key('name')->required()->lengthMin(10)
    ;
    $validation->errors()->first()->space(); // person.name

After nested() function targets are related to last target. Nested can be reset by unested(), group() or main() target. Nested can be composed in multiple levels downwards Space in error is automaticly generated.

Filters

Filters are applied before validators

Validation::new()
    ->key('name')
        ->filter(fn($x) => ucfirst($x)) // custom callback filter
        ->trim()
;

Required/Optional

Targets are optional by default. If target is required use required method.

Validation::new()
    ->key('name')->required()
;

Validators

Validation::new()
    ->key('name')
    // raw validator, validator return Result
    ->validator(fn ($value) => strlen($value) !== 13 ? Result::ofSuccess() : Result::ofErrorMsg('Length can not be 13'))
    // custom callback
    ->callback(fn ($value) => strlen($value) !== 13, 'Length can not be 13')
    ->notBlank('Fill name!')
;

All list validator in source

Scalars

$ok = Validation::of($email)->required()->email()->success();

If no target is provided, then the main target, validation subject, will be used.

Validation feedback

$v = Validation::new();
$v->result(); // Xtompie\Result\Result
$v->errors(); // Xtompie\Result\ErrorCollection
$v->error(); // ?Xtompie\Result\Error first error
$v->success(); // bool
$v->fail(); // bool

Extending

Component consists of 3 elements.

  1. ValidationValidator - builder and validator.
  2. ValidationCore - wrapper for the ValidationValidator. Gives fluent syntax, deals with validation subject.
  3. Validation - extends ValidationCore by inheritance. Gives concrete validations, filters, messages, keys.

Inheritance

Validation or ValidationCore can be extended by inheritance.

namespace App\Shared\Validation;

use App\Shared\Dao\Dao;
use Xtompie\Validation\Validation as BaseValidation;

class Validation extends BaseValidation
{
    public function __construct(
        protected Dao $dao,
    ) {}

    protected function msgs(): array
    {
        return array_merge(parent::msgs(), [
            'dao_not_exists' => 'Value {value} already exists',
        ]);
    }

    public function trim(): static
    {
        return $this->filter(fn($v) => trim($v));
    }

    public function digit($msg = 'Only digits allowed', $key = 'digit'): static
    {
        return $this->validator(fn($v) => ctype_digit($v) ? Result::ofSucces() : Result::ofErrorMsg($msg, $key));
    }

    public function daoNotExists(string $table, string $field, ?string $exceptId = null, ?string $msg = null)
    {
        return $this->validator(fn ($v) => $this->test(
            !$this->dao->exists($table, [$field => $v, 'id !=' => $exceptId]),
            'dao_not_exists',
            $msg,
            ['{value}' => $v]
        ));
    }
}

namespace App\User\Application\Service;
use App\Shared\Validation\Validation;

class CreateUserService
{
    public function __construct(
        protected Validation $validation,
    ) {}

    public function __invoke(string $email): Result
    {
        $result = $this->validation->withSubject($email)
            ->required()
            ->email()
            ->daoNotExists('user', 'email')
        ;
        if ($result->fail()) {
            return $result;
        }

        // create user

        return Result::ofSuccess();
    }
}