Skip to content

Commit 680c692

Browse files
committed
feat(validation): add ability to validate an array of values
1 parent 6579f35 commit 680c692

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

packages/validation/src/Validator.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@
1717

1818
use function Tempest\Support\arr;
1919

20+
use const Tempest\Support\Math\PI;
21+
2022
final readonly class Validator
2123
{
24+
/**
25+
* Validates the values of public properties on the specified object using attribute rules.
26+
*/
2227
public function validateObject(object $object): void
2328
{
2429
$class = new ClassReflector($object);
@@ -41,6 +46,8 @@ public function validateObject(object $object): void
4146
}
4247

4348
/**
49+
* Validates the specified `$values` for the corresponding public properties on the specified `$class`, using built-in PHP types and attribute rules.
50+
*
4451
* @param ClassReflector|class-string $class
4552
*/
4653
public function validateValuesForClass(ClassReflector|string $class, ?array $values, string $prefix = ''): array
@@ -89,6 +96,9 @@ class: $property->getType()->asClass(),
8996
return $failingRules;
9097
}
9198

99+
/**
100+
* Validates `$value` against the specified `$property`, using built-in PHP types and attribute rules.
101+
*/
92102
public function validateValueForProperty(PropertyReflector $property, mixed $value): array
93103
{
94104
$rules = $property->getAttributes(Rule::class);
@@ -113,6 +123,9 @@ public function validateValueForProperty(PropertyReflector $property, mixed $val
113123
return $this->validateValue($value, $rules);
114124
}
115125

126+
/**
127+
* Validates the specified `$value` against the specified set `$rules`.
128+
*/
116129
public function validateValue(mixed $value, Closure|Rule|array $rules): array
117130
{
118131
$failingRules = [];
@@ -132,6 +145,31 @@ public function validateValue(mixed $value, Closure|Rule|array $rules): array
132145
return $failingRules;
133146
}
134147

148+
/**
149+
* Validates the specified `$values` against the specified set `$rules`.
150+
* The `$rules` array is expected to have the same keys as `$values`, associated with instance of {@see Tempest\Validation\Rule}.
151+
* If `$rules` doesn't contain a key for a value, it will not be validated.
152+
*
153+
* @param array<string,mixed> $values
154+
* @param array<string,\Tempest\Validation\Rule> $rules
155+
*/
156+
public function validateValues(iterable $values, array $rules): array
157+
{
158+
$failingRules = [];
159+
160+
foreach ($values as $key => $value) {
161+
if (! array_key_exists($key, $rules)) {
162+
continue;
163+
}
164+
165+
if ($failures = $this->validateValue($value, $rules[$key])) {
166+
$failingRules[$key] = $failures;
167+
}
168+
}
169+
170+
return $failingRules;
171+
}
172+
135173
private function convertToRule(Rule|Closure $rule, mixed $value): Rule
136174
{
137175
if ($rule instanceof Rule) {

packages/validation/tests/ValidatorTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,41 @@ public function test_skip_validation_attribute(): void
238238

239239
$this->assertEmpty($failingRules);
240240
}
241+
242+
public function test_validate_values_some_invalid(): void
243+
{
244+
$failingRules = new Validator()->validateValues(
245+
[
246+
'name' => '',
247+
'email' => 'invalid-email',
248+
'age' => 0,
249+
],
250+
[
251+
'name' => [new IsString(), new NotNull()],
252+
'email' => [new Email()],
253+
'age' => [new IsInteger(), new NotNull()],
254+
],
255+
);
256+
257+
$this->assertCount(1, $failingRules);
258+
$this->assertInstanceOf(Email::class, $failingRules['email'][0]);
259+
}
260+
261+
public function test_validate_values_all_valid(): void
262+
{
263+
$failingRules = new Validator()->validateValues(
264+
[
265+
'name' => '',
266+
'email' => '[email protected]',
267+
'age' => 0,
268+
],
269+
[
270+
'name' => [new IsString(), new NotNull()],
271+
'email' => [new Email()],
272+
'age' => [new IsInteger(), new NotNull()],
273+
],
274+
);
275+
276+
$this->assertCount(0, $failingRules);
277+
}
241278
}

0 commit comments

Comments
 (0)