yiisoft/validator

Yii Validator

dev-master / 3.0.x-dev 2021-09-07 21:01 UTC

This package is auto-updated.

Last update: 2021-09-07 21:01:27 UTC


README

68747470733a2f2f796969736f66742e6769746875622e696f2f646f63732f696d616765732f7969695f6c6f676f2e737667

Yii Validator


The package provides data validation capabilities.

Latest Stable Version Total Downloads Build status Scrutinizer Code Quality Code Coverage Mutation testing badge static analysis type-coverage

Features

  • Could be used with any object.
  • Skip further validation if an error occurred for the same field.
  • Skip validation of empty value.
  • Error message formatting.
  • Conditional validation.
  • Could pass context to validation rule.
  • Common rules bundled.

Requirements

  • PHP 7.4 or higher.

Installation

The package could be installed with composer:

composer require yiisoft/validator --prefer-dist

General usage

Library could be used in two ways: validating a single value and validating a set of data.

Validating a single value

use Yiisoft\Validator\Rules;
use Yiisoft\Validator\Rule\Required;
use Yiisoft\Validator\Rule\Number;
use Yiisoft\Validator\Result;

$rules = new Rules([
    Required::rule(),
    Number::rule()->min(10),
    static function ($value): Result {
        $result = new Result();
        if ($value !== 42) {
            $result->addError('Value should be 42!');
        }
        return $result;
    }
]);

$result = $rules->validate(41);
if ($result->isValid() === false) {
    foreach ($result->getErrors() as $error) {
        // ...
    }
}

Validating a set of data

use Yiisoft\Validator\DataSetInterface;
use Yiisoft\Validator\Validator;
use Yiisoft\Validator\Rule\Number;
use Yiisoft\Validator\Result;

final class MoneyTransfer implements DataSetInterface
{
    private $amount;
    
    public function __construct($amount) {
        $this->amount = $amount;
    }
    
    public function getAttributeValue(string $key){
        if (!isset($this->$key)) {
            throw new \InvalidArgumentException("There is no \"$key\" in MoneyTransfer.");
        }
        
        return $this->$key;
    }
}

// Usually obtained from container
$validator = new Validator();

$moneyTransfer = new MoneyTransfer(142);

$rules = [    
    'amount' => [
        Number::rule()->integer()->max(100),
        static function ($value): Result {
            $result = new Result();
            if ($value === 13) {
                $result->addError('Value should not be 13!');
            }
            return $result;
        }
    ],
];

$results = $validator->validate($moneyTransfer, $rules);
foreach ($results as $attribute => $result) {
    if ($result->isValid() === false) {
        foreach ($result->getErrors() as $error) {
            // ...
        }
    }
}

Skipping validation on error

By default, if an error occurred during validation of an attribute, further rules for this attribute are skipped. To change this behavior use skipOnError(false) when configuring rules:

Number::rule()->integer()->max(100)->skipOnError(false)

Skipping empty values

By default, empty values are validated. That is undesirable if you need to allow not specifying a field. To change this behavior use skipOnEmpty(true):

Number::rule()->integer()->max(100)->skipOnEmpty(true)

Conditional validation

In some cases there is a need to apply rule conditionally. It could be performed by using when():

Number::rule()->integer()->min(100)->when(static function ($value, DataSetInterface $dataSet) {
    return $dataSet->getAttributeValue('country') === Country::USA;
});

If callable returns true rule is applied, when the value returned is false, rule is skipped.

Creating your own validation rules

To create your own validation rule you should extend Rule class:

namespace MyVendor\Rules;

use Yiisoft\Validator\DataSetInterface;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule;

final class Pi extends Rule
{
    protected function validateValue($value, DataSetInterface $dataSet = null): Result
    {
        $result = new Result();
        if ($value != M_PI) {
            $result->addError('Value is not PI');
        }
        return $result;
    }
}

Note that validateValue() second argument is an instance of DataSetInterface so you can use it if you need whole data set context. For example, implementation might be the following if you need to validate "company" property only if "hasCompany" is true:

namespace MyVendor\Rules;

use Yiisoft\Validator\DataSetInterface;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule;

final class CompanyName extends Rule
{
    protected function validateValue($value, DataSetInterface $dataSet = null): Result
    {
        $result = new Result();
        $hasCompany = $dataSet !== null && $dataSet->getAttributeValue('hasCompany') === true;

        if ($hasCompany && $this->isCompanyNameValid($value) === false) {
            
            $result->addError('Company name is not valid');
        }
        return $result;
    }
    
    private function isCompanyNameValid(string $value): bool
    {
        // check company name    
    }
}

Grouping multiple validation rules

To reuse multiple validation rules it is advised to group rules like the following:

use Yiisoft\Validator\Rules;
use Yiisoft\Validator\Rule\HasLength;
use Yiisoft\Validator\Rule\MatchRegularExpression;
use \Yiisoft\Validator\Rule\GroupRule;

final class UsernameRule extends GroupRule
{
    public function getRules(): Rules
    {
        return new Rules([
            HasLength::rule()->min(2)->max(20),
            MatchRegularExpression::rule('~[a-z_\-]~i')
        ]);
    }
}

Then it could be used like the following:

use Yiisoft\Validator\Validator;
use Yiisoft\Validator\Rule\Email;

$validator = new Validator();

$rules= [
    'username' => UsernameRule::rule(),
    'email' => [Email::rule()]
];
$results = $validator->validate($user, $rules);
foreach ($results as $attribute => $result) {
    if ($result->isValid() === false) {
        foreach ($result->getErrors() as $error) {
            // ...
        }
    }
}

Setting up your own formatter

If you want to customize error message formatter in a certain case you need to use immutable withFormatter() method:

use Yiisoft\Validator\Validator;

final class PostController
{
    public function actionIndex(Validator $validator): ResponseInterface
    {
        // ...
        $result = $validator->withFormatter(new CustomFormatter())->validate($dataSet, $rules);
        // ...
    }
}

Testing

Unit testing

The package is tested with PHPUnit. To run tests:

./vendor/bin/phpunit

Mutation testing

The package tests are checked with Infection mutation framework with Infection Static Analysis Plugin. To run it:

./vendor/bin/roave-infection-static-analysis-plugin

Static analysis

The code is statically analyzed with Psalm. To run static analysis:

./vendor/bin/psalm

License

The Yii Validator is free software. It is released under the terms of the BSD License. Please see LICENSE for more information.

Maintained by Yii Software.

Support the project

Open Collective

Follow updates

Official website Twitter Telegram Facebook Slack