Skip to content

Constraints

Each Constraint have the following methods:

  • ->__invoke()
  • ->and()
  • ->or()
  • ->map()
  • ->asPredicate()

->__invoke()

This method is the one to apply the validation on an input and will return a Validation monad that will contain either the validated data or the error messages.

Let's take a simple example to check if the input of a method is a string:

use Innmind\Validation\Is;

function(mixed $input): string {
    $validate = Is::string();

    return $validate($input)->match(
        static fn(string $value) => $value,
        static fn() => throw new \RuntimeException('Input is not a string');
    );
}
Info

Note that we use the style $validate($input) and not $validate->__invoke($input). This style allows to treat the constraints as if it were native functions.

->and()

This method allows to apply extra validation on an input.

Let's take the example of making sure a string is shorter than 255 characters:

use Innmind\Validation\{
    Is,
    Of,
    Failure,
};
use Innmind\Immutable\Validation;

function(mixed $input): string {
    $validate = Is::string()->and(Of::callable(
        static fn(string $value) => match (true) {
            \strlen($value) < 255 => Validation::success($value),
            default => Validation::fail(Failure::of('String is too long')),
        },
    ));

    return $validate($input)->match(
        static fn(string $value) => $value,
        static fn() => throw new \RuntimeException('Input is not valid');
    );
}

->or()

This method allows to have an alternate validation in case the first one fails. This is useful for unions types.

Let's take the example where the input needs to be a string or an int:

use Innmind\Validation\Is;

function(mixed $input): string|int {
    $validate = Is::string()->or(Is::int());

    return return $validate($input)->match(
        static fn(string|int $value) => $value,
        static fn() => throw new \RuntimeException('Input is not valid');
    );
}

->map()

This method allows to transform the validated value to anything you want.

Let's take the example where a need a string but want to output a Str for ease of use afterwards:

use Innmind\Validation\Is;
use Innmind\Immutable\Str;

function(mixed $input): Str {
    $validate = Is::string()->map(
        static fn(string $value) => Str::of($value),
    );

    return return $validate($input)->match(
        static fn(Str $value) => $value,
        static fn() => throw new \RuntimeException('Input is not valid');
    );
}

->asPredicate()

A Predicate acts as a function that returns a bool.

It's intended to be used with Sequence::keep() or Set::keep().

Here's an example to keep all strings inside a Sequence:

use Innmind\Validation\Is;
use Innmind\Immutable\Sequence;

Sequence::of(1, 'a', null, 'b', new \stdClass, 'c')
    ->keep(Is::string()->asPredicate())
    ->toList(); // returns ['a', 'b', 'c']
Note

There's no need to apply transformations on your constraints when used as predicates as the outputed value is not used.

Handling failures

In the examples above we've thrown exceptions in case of errors but you have access to all the failures messages and where they happened.

use Innmind\Validation\{
    Is,
    Failure,
};
use Innmind\Immutable\Sequence;

$validate = Is::shape('id', Is::int())
    ->with('username', Is::string());

$validate($input)->match(
    static fn(array $valid) => $valid,
    static fn(Sequence $failures) => \var_dump(
        $failures
            ->map(static fn(Failure $failure) => [
                $failure->path()->toString(),
                $failure->message(),
            ])
            ->toList(),
    ),
);

In case $input is invalid it will print:

[['$', 'Value is not of type array']]
[
    ['$', 'The key id is missing'],
    ['$', 'The key username is missing'],
]
[
    ['id', 'Value is not of type int'],
    ['username', 'Value is not of type string'],
]