Skip to content

Commit 5f41a85

Browse files
authored
#115: Add rules provider
1 parent d3f54de commit 5f41a85

9 files changed

Lines changed: 213 additions & 53 deletions
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Validator;
6+
7+
/**
8+
* Allows to implement post-validation processing in the data set object itself.
9+
*
10+
* @see \Yiisoft\Form\FormModel::processValidationResult
11+
*/
12+
interface PostValidationHookInterface extends DataSetInterface
13+
{
14+
public function processValidationResult(ResultSet $resultSet): void;
15+
}

src/RulesProviderInterface.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Validator;
6+
7+
/**
8+
* Allows to have data validation rules together with the data itself.
9+
* Such object can be passed as an only argument to the {@see ValidatorInterface}.
10+
*/
11+
interface RulesProviderInterface extends DataSetInterface
12+
{
13+
/**
14+
* @return iterable A set of validation rules.
15+
*/
16+
public function getRules(): iterable;
17+
}

src/Validator.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,34 @@ public function __construct(?FormatterInterface $formatter = null)
1717
}
1818

1919
/**
20-
* @param DataSetInterface $dataSet
20+
* @param DataSetInterface|RulesProviderInterface $dataSet
2121
* @param Rule[][] $rules
2222
* @psalm-param iterable<string, Rule[]> $rules
2323
*
2424
* @return ResultSet
2525
*/
26-
public function validate(DataSetInterface $dataSet, iterable $rules): ResultSet
26+
public function validate(DataSetInterface $dataSet, iterable $rules = []): ResultSet
2727
{
28+
if ($dataSet instanceof RulesProviderInterface) {
29+
/** @noinspection CallableParameterUseCaseInTypeContextInspection */
30+
$rules = $dataSet->getRules();
31+
}
2832
$context = new ValidationContext($dataSet);
29-
$results = new ResultSet();
33+
$resultSet = new ResultSet();
3034
foreach ($rules as $attribute => $attributeRules) {
3135
$aggregateRule = new Rules($attributeRules);
3236
if ($this->formatter !== null) {
3337
$aggregateRule = $aggregateRule->withFormatter($this->formatter);
3438
}
35-
$results->addResult(
39+
$resultSet->addResult(
3640
$attribute,
3741
$aggregateRule->validate($dataSet->getAttributeValue($attribute), $context->withAttribute($attribute))
3842
);
3943
}
40-
return $results;
44+
if ($dataSet instanceof PostValidationHookInterface) {
45+
$dataSet->processValidationResult($resultSet);
46+
}
47+
return $resultSet;
4148
}
4249

4350
public function withFormatter(?FormatterInterface $formatter): self

src/ValidatorInterface.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ interface ValidatorInterface
1212
/**
1313
* Validate data set against rules set for data set attributes.
1414
*
15-
* @param DataSetInterface $dataSet Data set to validate.
15+
* @param DataSetInterface|RulesProviderInterface $dataSet Data set to validate.
1616
* @param Rule[][] $rules Rules to apply.
1717
* @psalm-param iterable<string, Rule[]> $rules
1818
*
1919
* @return ResultSet Validation results.
2020
*/
21-
public function validate(DataSetInterface $dataSet, iterable $rules): ResultSet;
21+
public function validate(DataSetInterface $dataSet, iterable $rules = []): ResultSet;
2222
}

tests/AbstractDataSetTest.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Validator\Tests;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Yiisoft\Validator\Result;
9+
use Yiisoft\Validator\ResultSet;
10+
use Yiisoft\Validator\Rule\Boolean;
11+
use Yiisoft\Validator\Rule\Number;
12+
13+
abstract class AbstractDataSetTest extends TestCase
14+
{
15+
/**
16+
* @dataProvider validationCasesDataProvider()
17+
*
18+
* @param array $dataSet
19+
*/
20+
final public function test(array $dataSet, array $rules): void
21+
{
22+
$resultSet = $this->validate($dataSet, $rules);
23+
24+
$this->assertTrue($resultSet->isValid());
25+
}
26+
27+
public function validationCasesDataProvider(): array
28+
{
29+
return [
30+
[
31+
[
32+
'bool' => true,
33+
'int' => 41,
34+
],
35+
[
36+
'bool' => [new Boolean()],
37+
'int' => [new Number()],
38+
],
39+
],
40+
];
41+
}
42+
43+
/**
44+
* @dataProvider resultDataProvider()
45+
*
46+
* @param array $dataSet
47+
*/
48+
public function testResult(array $dataSet, array $rules): void
49+
{
50+
$resultSet = $this->validate($dataSet, $rules);
51+
52+
$this->assertTrue($resultSet->getResult('bool')->isValid());
53+
54+
$intResult = $resultSet->getResult('int');
55+
$this->assertFalse($intResult->isValid());
56+
$this->assertCount(1, $intResult->getErrors());
57+
}
58+
59+
public function resultDataProvider(): array
60+
{
61+
return [
62+
[
63+
[
64+
'bool' => true,
65+
'int' => 41,
66+
],
67+
[
68+
'bool' => [new Boolean()],
69+
'int' => [
70+
(new Number())->integer(),
71+
(new Number())->integer()->min(44),
72+
static function ($value): Result {
73+
$result = new Result();
74+
if ($value !== 42) {
75+
$result->addError('Value should be 42!');
76+
}
77+
return $result;
78+
},
79+
],
80+
],
81+
],
82+
];
83+
}
84+
85+
abstract protected function validate(array $dataSet, array $rules): ResultSet;
86+
}

tests/DataSetTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Validator\Tests;
6+
7+
use Yiisoft\Validator\ResultSet;
8+
use Yiisoft\Validator\Tests\Stub\DataSet;
9+
use Yiisoft\Validator\Validator;
10+
11+
final class DataSetTest extends AbstractDataSetTest
12+
{
13+
protected function validate(array $dataSet, array $rules): ResultSet
14+
{
15+
$dataObject = new DataSet($dataSet);
16+
17+
$validator = new Validator();
18+
19+
return $validator->validate($dataObject, $rules);
20+
}
21+
}

tests/RulesProvidedDataSetTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Validator\Tests;
6+
7+
use Yiisoft\Validator\ResultSet;
8+
use Yiisoft\Validator\Tests\Stub\RulesProvidedDataSet;
9+
use Yiisoft\Validator\Validator;
10+
11+
final class RulesProvidedDataSetTest extends AbstractDataSetTest
12+
{
13+
protected function validate(array $dataSet, array $rules): ResultSet
14+
{
15+
$dataObject = new RulesProvidedDataSet($dataSet, $rules);
16+
17+
$validator = new Validator();
18+
19+
return $validator->validate($dataObject);
20+
}
21+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Validator\Tests\Stub;
6+
7+
use Yiisoft\Validator\Exception\MissingAttributeException;
8+
use Yiisoft\Validator\RulesProviderInterface;
9+
10+
final class RulesProvidedDataSet implements RulesProviderInterface
11+
{
12+
private array $data;
13+
private array $rules;
14+
15+
public function __construct(array $data, array $rules)
16+
{
17+
$this->data = $data;
18+
$this->rules = $rules;
19+
}
20+
21+
public function getAttributeValue(string $attribute)
22+
{
23+
if (!$this->hasAttribute($attribute)) {
24+
throw new MissingAttributeException("There is no \"$attribute\" attribute in the class.");
25+
}
26+
27+
return $this->data[$attribute];
28+
}
29+
30+
public function hasAttribute(string $attribute): bool
31+
{
32+
return isset($this->data[$attribute]);
33+
}
34+
35+
public function getRules(): iterable
36+
{
37+
return $this->rules;
38+
}
39+
}

tests/ValidatorTest.php

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)