diff --git a/src/Result.php b/src/Result.php index 26bd0d3..319b46f 100644 --- a/src/Result.php +++ b/src/Result.php @@ -9,7 +9,8 @@ public function __construct( protected bool $outcome, protected ?string $success = null, - protected ?string $failure = null + protected ?string $failure = null, + protected ?array $messages = null, ) { } @@ -23,8 +24,12 @@ public function failed(): bool return $this->outcome !== true; } - public function message(): ?string + public function message(): string|array|null { + if ($this->messages) { + return $this->messages; + } + return $this->passed() ? $this->success : $this->failure; } diff --git a/src/Validated.php b/src/Validated.php index b8cd41e..13fc392 100644 --- a/src/Validated.php +++ b/src/Validated.php @@ -46,7 +46,8 @@ public function property(string $property, string $rule = null): Result if (! $rule) { $filter = array_filter($result, fn (Result $result) => $result->failed()); - return new Result(count($filter) === 0); + $messages = array_map(fn (Result $result) => $result->message(), $result); + return new Result(count($filter) === 0, messages: $messages); } if (! array_key_exists($rule, $result)) { @@ -67,4 +68,14 @@ public function failed(): bool { return ! $this->passed(); } + + public function messages(): array + { + $messages = []; + foreach ($this->properties as $property) { + $messages[$property->name] = $this->property($property->name)->message(); + } + + return $messages; + } } diff --git a/src/Validator.php b/src/Validator.php index f0ba928..831d055 100644 --- a/src/Validator.php +++ b/src/Validator.php @@ -20,13 +20,13 @@ public function __construct(protected mixed $source) public function validate(): Validated { $properties = (new ReflectionClass($this->source))->getProperties(ReflectionProperty::IS_PUBLIC); - $errors = []; + $results = []; foreach ($properties as $property) { - $errors[$property->getName()] = $this->validateProperty($property); + $results[$property->getName()] = $this->validateProperty($property); } - return new Validated($properties, $errors); + return new Validated($properties, $results); } protected function validateProperty(ReflectionProperty $property): array diff --git a/tests/ObjectValidationTest.php b/tests/ObjectValidationTest.php index b066e88..e2cd4de 100644 --- a/tests/ObjectValidationTest.php +++ b/tests/ObjectValidationTest.php @@ -76,6 +76,41 @@ ->and((string)$validated->property('name', Rules\Required::class))->toEqual('success message'); }); +it('can get all validation messages for a property', function () { + $option = new class { + #[Rules\Required(success: 'Has email'), Rules\Email(failure: 'Not a valid email')] + public string $email = 'test@bad-email'; + }; + + $validated = (new Validator($option))->validate(); + + expect($validated->property('email')->message())->toEqual([ + Rules\Required::class => 'Has email', + Rules\Email::class => 'Not a valid email', + ]); +}); + +it('can get all the validation messages for an object', function () { + $option = new class { + #[Rules\Required(failure: 'Name is required')] + public string $name = ''; + #[Rules\Required(success: 'Has email'), Rules\Email(failure: 'Not a valid email')] + public string $email = 'test@bad-email'; + }; + + $validated = (new Validator($option))->validate(); + + expect($validated->messages())->toEqual([ + 'name' => [ + Rules\Required::class => 'Name is required', + ], + 'email' => [ + Rules\Required::class => 'Has email', + Rules\Email::class => 'Not a valid email', + ], + ]); +}); + it('can stop on a failed rule marked as last', function () { $object = new class () {