Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 7c70175

Browse files
authored
Merge pull request #12 from programmatordev/YAPV-7-create-all-rule
Create All rule
2 parents f550c97 + d92755b commit 7c70175

File tree

11 files changed

+207
-13
lines changed

11 files changed

+207
-13
lines changed

src/ChainedValidatorInterface.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ public function validate(mixed $value): bool;
1515

1616
// --- Rules ---
1717

18-
public function notBlank(array $options = []): ChainedValidatorInterface;
18+
public function all(array $constraints, array $options = null): ChainedValidatorInterface;
19+
20+
public function choice(array $constraints, bool $multiple = false, ?int $minConstraint = null, ?int $maxConstraint = null, array $options = []): ChainedValidatorInterface;
1921

2022
public function greaterThan(mixed $constraint, array $options = []): ChainedValidatorInterface;
2123

@@ -25,7 +27,7 @@ public function lessThan(mixed $constraint, array $options = []): ChainedValidat
2527

2628
public function lessThanOrEqual(mixed $constraint, array $options = []): ChainedValidatorInterface;
2729

28-
public function choice(array $constraints, bool $multiple = false, ?int $minConstraint = null, ?int $maxConstraint = null, array $options = []): ChainedValidatorInterface;
30+
public function notBlank(array $options = []): ChainedValidatorInterface;
2931

3032
public function range(mixed $minConstraint, mixed $maxConstraint, array $options = []): ChainedValidatorInterface;
3133
}

src/Exception/AllException.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\YetAnotherPhpValidator\Exception;
4+
5+
class AllException extends ValidationException {}

src/Rule/All.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\YetAnotherPhpValidator\Rule;
4+
5+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\AllException;
6+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\UnexpectedValueException;
7+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\ValidationException;
8+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\Util\AssertIsValidatableTrait;
9+
use Symfony\Component\OptionsResolver\OptionsResolver;
10+
11+
class All extends AbstractRule implements RuleInterface
12+
{
13+
use AssertIsValidatableTrait;
14+
15+
private array $options;
16+
17+
/**
18+
* @param RuleInterface[] $constraints
19+
*/
20+
public function __construct(
21+
private readonly array $constraints,
22+
array $options = []
23+
)
24+
{
25+
$resolver = new OptionsResolver();
26+
27+
$resolver->setDefaults(['message' => 'At "{{ key }}": {{ message }}']);
28+
29+
$resolver->setAllowedTypes('message', 'string');
30+
31+
$this->options = $resolver->resolve($options);
32+
}
33+
34+
public function assert(mixed $value, string $name): void
35+
{
36+
$this->assertIsValidatable($this->constraints);
37+
38+
if (!\is_array($value)) {
39+
throw new UnexpectedValueException(
40+
\sprintf('Expected value of type "array", "%s" given.', get_debug_type($value))
41+
);
42+
}
43+
44+
try {
45+
foreach ($value as $key => $input) {
46+
foreach ($this->constraints as $constraint) {
47+
$constraint->assert($input, $name);
48+
}
49+
}
50+
}
51+
catch (ValidationException $exception) {
52+
throw new AllException(
53+
message: $this->options['message'],
54+
parameters: [
55+
'value' => $value,
56+
'name' => $name,
57+
'key' => $key,
58+
'message' => $exception->getMessage()
59+
]
60+
);
61+
}
62+
}
63+
}

src/Rule/Choice.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function assert(mixed $value, string $name): void
3939
{
4040
if ($this->multiple && !\is_array($value)) {
4141
throw new UnexpectedValueException(
42-
\sprintf('Expected value of type "array" when multiple, "%s" given', get_debug_type($value))
42+
\sprintf('Expected value of type "array" when using multiple choices, "%s" given', get_debug_type($value))
4343
);
4444
}
4545

src/Rule/Range.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ public function __construct(
2222
{
2323
$resolver = new OptionsResolver();
2424

25-
$resolver->setDefaults([
26-
'message' => 'The "{{ name }}" value should be between "{{ minConstraint }}" and "{{ maxConstraint }}", "{{ value }}" given.'
27-
]);
25+
$resolver->setDefaults(['message' => 'The "{{ name }}" value should be between "{{ minConstraint }}" and "{{ maxConstraint }}", "{{ value }}" given.']);
2826

2927
$resolver->setAllowedTypes('message', 'string');
3028

@@ -35,13 +33,22 @@ public function assert(mixed $value, string $name): void
3533
{
3634
$this->assertIsComparable($this->minConstraint, $this->maxConstraint);
3735

38-
if (!Validator::greaterThan($this->minConstraint)->validate($this->maxConstraint)) {
36+
if (
37+
!Validator
38+
::greaterThan($this->minConstraint)
39+
->validate($this->maxConstraint)
40+
) {
3941
throw new UnexpectedValueException(
4042
'Max constraint value must be greater than min constraint value.'
4143
);
4244
}
4345

44-
if (!Validator::greaterThanOrEqual($this->minConstraint)->lessThanOrEqual($this->maxConstraint)->validate($value)) {
46+
if (
47+
!Validator
48+
::greaterThanOrEqual($this->minConstraint)
49+
->lessThanOrEqual($this->maxConstraint)
50+
->validate($value)
51+
) {
4552
throw new RangeException(
4653
message: $this->options['message'],
4754
parameters: [

src/Rule/Util/AssertIsComparableTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ private function assertIsComparable(mixed $value1, mixed $value2): bool
2222

2323
throw new UnexpectedValueException(
2424
\sprintf(
25-
'Cannot compare a type "%s" with a type "%s"',
25+
'Cannot compare a type "%s" with a type "%s".',
2626
get_debug_type($value1),
2727
get_debug_type($value2)
2828
)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\YetAnotherPhpValidator\Rule\Util;
4+
5+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\UnexpectedValueException;
6+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\RuleInterface;
7+
8+
trait AssertIsValidatableTrait
9+
{
10+
private function assertIsValidatable(array $constraints): bool
11+
{
12+
foreach ($constraints as $constraint) {
13+
if (!$constraint instanceof RuleInterface) {
14+
throw new UnexpectedValueException(
15+
\sprintf('Expected constraint of type "RuleInterface", "%s" given.', get_debug_type($constraint))
16+
);
17+
}
18+
}
19+
20+
return true;
21+
}
22+
}

src/StaticValidatorInterface.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
interface StaticValidatorInterface
66
{
7-
public static function notBlank(array $options = []): ChainedValidatorInterface;
7+
public static function all(array $constraints, array $options = null): ChainedValidatorInterface;
8+
9+
public static function choice(array $constraints, bool $multiple = false, ?int $minConstraint = null, ?int $maxConstraint = null, array $options = []): ChainedValidatorInterface;
810

911
public static function greaterThan(mixed $constraint, array $options = []): ChainedValidatorInterface;
1012

@@ -14,7 +16,7 @@ public static function lessThan(mixed $constraint, array $options = []): Chained
1416

1517
public static function lessThanOrEqual(mixed $constraint, array $options = []): ChainedValidatorInterface;
1618

17-
public static function choice(array $constraints, bool $multiple = false, ?int $minConstraint = null, ?int $maxConstraint = null, array $options = []): ChainedValidatorInterface;
19+
public static function notBlank(array $options = []): ChainedValidatorInterface;
1820

1921
public static function range(mixed $minConstraint, mixed $maxConstraint, array $options = []): ChainedValidatorInterface;
2022
}

src/Validator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
/**
1111
* @mixin StaticValidatorInterface
1212
*/
13-
class Validator
13+
class Validator implements RuleInterface
1414
{
1515
private array $rules;
1616

tests/AllTest.php

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\YetAnotherPhpValidator\Test;
4+
5+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\AllException;
6+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\All;
7+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\GreaterThan;
8+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\NotBlank;
9+
use ProgrammatorDev\YetAnotherPhpValidator\Test\Util\TestRuleFailureConditionTrait;
10+
use ProgrammatorDev\YetAnotherPhpValidator\Test\Util\TestRuleMessageOptionTrait;
11+
use ProgrammatorDev\YetAnotherPhpValidator\Test\Util\TestRuleSuccessConditionTrait;
12+
use ProgrammatorDev\YetAnotherPhpValidator\Test\Util\TestRuleUnexpectedValueTrait;
13+
use ProgrammatorDev\YetAnotherPhpValidator\Validator;
14+
15+
class AllTest extends AbstractTest
16+
{
17+
use TestRuleUnexpectedValueTrait;
18+
use TestRuleFailureConditionTrait;
19+
use TestRuleSuccessConditionTrait;
20+
use TestRuleMessageOptionTrait;
21+
22+
public static function provideRuleUnexpectedValueData(): \Generator
23+
{
24+
yield 'invalid constraint' => [
25+
new All([new NotBlank(), 'invalid']),
26+
[1, 2, 3],
27+
'/Expected constraint of type "RuleInterface", "(.*)" given./'
28+
];
29+
yield 'invalid value type' => [
30+
new All([new NotBlank()]),
31+
'invalid',
32+
'/Expected value of type "array", "(.*)" given./'
33+
];
34+
yield 'unexpected value propagation' => [
35+
new All([new GreaterThan(10)]),
36+
['a'],
37+
'/Cannot compare a type "(.*)" with a type "(.*)"./'
38+
];
39+
}
40+
41+
public static function provideRuleFailureConditionData(): \Generator
42+
{
43+
$exception = AllException::class;
44+
$message = '/At "(.*)": The "(.*)" value should not be blank, "(.*)" given./';
45+
46+
yield 'constraint' => [
47+
new All([new NotBlank()]),
48+
[1, 2, ''],
49+
$exception,
50+
$message
51+
];
52+
yield 'validator' => [
53+
new All([(new Validator(new NotBlank()))]),
54+
[1, 2, ''],
55+
$exception,
56+
$message
57+
];
58+
}
59+
60+
public static function provideRuleSuccessConditionData(): \Generator
61+
{
62+
yield 'constraints' => [
63+
new All([new NotBlank(), new GreaterThan(1)]),
64+
[2, 3, 4]
65+
];
66+
yield 'validators' => [
67+
new All([
68+
(new Validator(new NotBlank())),
69+
(new Validator(new GreaterThan(1)))
70+
]),
71+
[2, 3, 4]
72+
];
73+
yield 'constraints and validators' => [
74+
new All([
75+
new NotBlank(),
76+
(new Validator(new GreaterThan(1)))
77+
]),
78+
[2, 3, 4]
79+
];
80+
}
81+
82+
public static function provideRuleMessageOptionData(): \Generator
83+
{
84+
yield 'constraint' => [
85+
new All(
86+
constraints: [new NotBlank()],
87+
options: [
88+
'message' => 'The "{{ name }}" value "{{ value }}" failed at key "{{ key }}".'
89+
]
90+
), [1, 2, ''], 'The "test" value "[1, 2, ]" failed at key "2".'
91+
];
92+
}
93+
}

0 commit comments

Comments
 (0)