/validator

Yii validator library

Primary LanguagePHPBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

Yii Validator


The package provides data validation capabilities.

Latest Stable Version Total Downloads Build Status Scrutinizer Code Quality

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 translations.
  • Conditional validation.
  • Could pass context to validation rule.
  • Common rules bundled.

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([
    new Required(),
    (new Number())->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;

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;
    }
}

$moneyTransfer = new MoneyTransfer(142);

$validator = new Validator([    
    'amount' => [
        (new Number())->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);
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:

(new Number())->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):

(new Number())->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():

(new Number())->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;

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;

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 common validation rules into rule sets

To reuse multiple validation rules it is advised to group rules into validation sets:

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

class UsernameRules
{
    public static function get(): array
    {
        return [
            (new HasLength())->min(2)->max(20),
            new MatchRegularExpression('~[a-z_\-]~i')
        ];
    }
}

Then it could be used like the following:

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

$validator = new Validator([    
    'username' => UsernameRules::get(),
    'email' => [new Email()]
]);

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