From 999c301da974c656c8c838be1e9cae2001c91cd8 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 12 Apr 2024 12:02:01 +0500 Subject: [PATCH 1/7] Add methods for getting first error messages to `Result` --- src/Result.php | 54 ++++++++++++++++++++++++++++++++++++++++++++ tests/ResultTest.php | 36 +++++++++++++++++++++-------- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/Result.php b/src/Result.php index 4b1f16451..8430ed36e 100644 --- a/src/Result.php +++ b/src/Result.php @@ -6,6 +6,7 @@ use InvalidArgumentException; +use function array_key_exists; use function array_slice; use function implode; use function is_string; @@ -94,6 +95,33 @@ public function getErrorMessagesIndexedByPath(string $separator = '.', ?string $ return $errors; } + /** + * Get strings of the first error messages for each attribute path. + * Each key is a dot-separated attribute path. + * Each value is the first error message string for this path. + * + * @param string $separator Attribute path separator. Dot is used by default. + * @param string|null $escape Symbol that will be escaped with a backslash char (`\`) in path elements. + * When it's null path is returned without escaping. + * + * @return array Strings of error messages indexed by attribute path. + * + * @psalm-return array + */ + public function getFirstErrorMessagesIndexedByPath(string $separator = '.', ?string $escape = '.'): array + { + $errors = []; + foreach ($this->errors as $error) { + $stringValuePath = implode($separator, $error->getValuePath($escape)); + + if (!array_key_exists($stringValuePath, $errors)) { + $errors[$stringValuePath] = $error->getMessage(); + } + } + + return $errors; + } + /** * Get arrays of error messages indexed by attribute name. * @@ -118,6 +146,32 @@ public function getErrorMessagesIndexedByAttribute(): array return $errors; } + /** + * Get arrays of the first error messages for each attribute name. + * + * @throws InvalidArgumentException If top level attribute has a non-string type. + * + * @return array Strings of error messages indexed by attribute name. + * + * @psalm-return array + */ + public function getFirstErrorMessagesIndexedByAttribute(): array + { + $errors = []; + foreach ($this->errors as $error) { + $key = $error->getValuePath()[0] ?? ''; + if (!is_string($key)) { + throw new InvalidArgumentException('Top level attributes can only have string type.'); + } + + if (!array_key_exists($key, $errors)) { + $errors[$key] = $error->getMessage(); + } + } + + return $errors; + } + /** * Get an array of error objects for the attribute specified. * diff --git a/tests/ResultTest.php b/tests/ResultTest.php index 4ff5b2bd2..f7bc3699f 100644 --- a/tests/ResultTest.php +++ b/tests/ResultTest.php @@ -58,14 +58,6 @@ public function testGetErrorMessages(): void } public function testGetErrorMessagesIndexedByPath(): void - { - $this->assertEquals( - ['' => ['error1'], 'path.2' => ['error2']], - $this->createErrorResult()->getErrorMessagesIndexedByPath() - ); - } - - public function testGetErrorMessagesIndexedByPathWithAttributes(): void { $this->assertEquals( [ @@ -79,6 +71,20 @@ public function testGetErrorMessagesIndexedByPathWithAttributes(): void ); } + public function testGetFirstErrorMessagesIndexedByPath(): void + { + $this->assertSame( + [ + 'attribute2' => 'error2.1', + 'attribute2.nested' => 'error2.3', + '' => 'error3.1', + 'attribute4.subattribute4\.1.subattribute4*2' => 'error4.1', + 'attribute4.subattribute4\.3.subattribute4*4' => 'error4.2', + ], + $this->createAttributeErrorResult()->getFirstErrorMessagesIndexedByPath(), + ); + } + public function testIsAttributeValid(): void { $result = $this->createAttributeErrorResult(); @@ -110,6 +116,18 @@ public function testGetErrorMessagesIndexedByAttribute_IncorrectType(): void $result->getErrorMessagesIndexedByAttribute(); } + public function testGetFirstErrorMessagesIndexedByAttribute(): void + { + $this->assertSame( + [ + 'attribute2' => 'error2.1', + '' => 'error3.1', + 'attribute4' => 'error4.1', + ], + $this->createAttributeErrorResult()->getFirstErrorMessagesIndexedByAttribute(), + ); + } + public function testGetAttributeErrors(): void { $result = $this->createAttributeErrorResult(); @@ -269,7 +287,7 @@ private function createErrorResult(): Result $result = new Result(); $result ->addError('error1') - ->addError('error2', [], ['path', 2]); + ->addError('error2', valuePath: ['path', 2]); return $result; } From e2ad818b86ee1e92540890ee254da9aae356cc74 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 12 Apr 2024 12:41:11 +0500 Subject: [PATCH 2/7] Bump coverage --- tests/ResultTest.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/ResultTest.php b/tests/ResultTest.php index f7bc3699f..d659f15f6 100644 --- a/tests/ResultTest.php +++ b/tests/ResultTest.php @@ -106,13 +106,12 @@ public function testGetErrorMessagesIndexedByAttribute(): void ); } - public function testGetErrorMessagesIndexedByAttribute_IncorrectType(): void + public function testGetErrorMessagesIndexedByAttributeWithIncorrectType(): void { - $result = new Result(); - - $result->addError('error1', [], [1]); + $result = (new Result())->addError('error1', [], [1]); $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Top level attributes can only have string type.'); $result->getErrorMessagesIndexedByAttribute(); } @@ -128,6 +127,15 @@ public function testGetFirstErrorMessagesIndexedByAttribute(): void ); } + public function testGetFirstErrorMessagesIndexedByAttributeWithIncorrectType(): void + { + $result = (new Result())->addError('error1', [], [1]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Top level attributes can only have string type.'); + $result->getFirstErrorMessagesIndexedByAttribute(); + } + public function testGetAttributeErrors(): void { $result = $this->createAttributeErrorResult(); From 72fe0cf6643fe86da472a7ebf55c7efe2d383918 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 12 Apr 2024 14:05:43 +0500 Subject: [PATCH 3/7] Update src/Result.php Co-authored-by: Sergei Predvoditelev --- src/Result.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Result.php b/src/Result.php index 8430ed36e..4e7b36310 100644 --- a/src/Result.php +++ b/src/Result.php @@ -164,9 +164,7 @@ public function getFirstErrorMessagesIndexedByAttribute(): array throw new InvalidArgumentException('Top level attributes can only have string type.'); } - if (!array_key_exists($key, $errors)) { - $errors[$key] = $error->getMessage(); - } + $errors[$key] ??= $error->getMessage(); } return $errors; From 74562ce807137aaa7ab6484c95acb8632f8070bf Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 12 Apr 2024 14:05:48 +0500 Subject: [PATCH 4/7] Update src/Result.php Co-authored-by: Sergei Predvoditelev --- src/Result.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Result.php b/src/Result.php index 4e7b36310..789619318 100644 --- a/src/Result.php +++ b/src/Result.php @@ -114,9 +114,7 @@ public function getFirstErrorMessagesIndexedByPath(string $separator = '.', ?str foreach ($this->errors as $error) { $stringValuePath = implode($separator, $error->getValuePath($escape)); - if (!array_key_exists($stringValuePath, $errors)) { - $errors[$stringValuePath] = $error->getMessage(); - } + $errors[$stringValuePath] ??= $error->getMessage(); } return $errors; From 8dfcc9c7c7143cb3bda14bc72f97471e5cb94d0c Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 12 Apr 2024 09:05:54 +0000 Subject: [PATCH 5/7] Apply fixes from StyleCI --- src/Result.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Result.php b/src/Result.php index 789619318..a9f917cfb 100644 --- a/src/Result.php +++ b/src/Result.php @@ -6,7 +6,6 @@ use InvalidArgumentException; -use function array_key_exists; use function array_slice; use function implode; use function is_string; From 7dd121274e6a9e39fa3af2754030de915ffbf528 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 12 Apr 2024 14:06:38 +0500 Subject: [PATCH 6/7] Remove newline [skip ci] --- src/Result.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Result.php b/src/Result.php index 789619318..53af3c184 100644 --- a/src/Result.php +++ b/src/Result.php @@ -113,7 +113,6 @@ public function getFirstErrorMessagesIndexedByPath(string $separator = '.', ?str $errors = []; foreach ($this->errors as $error) { $stringValuePath = implode($separator, $error->getValuePath($escape)); - $errors[$stringValuePath] ??= $error->getMessage(); } From 161451c2aedaab27f32902180154c433bf5166b8 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Fri, 12 Apr 2024 14:19:39 +0500 Subject: [PATCH 7/7] Add changelog entry [skip ci] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 205672f6a..7761d0ccf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - New #657: Add `Result::add()` method for merging other results to the base one (@arogachev) - New #655: Add rules for validating value types - `boolean`, `float`, `integer`, `string` (@arogachev) - New #693: Add `AnyRule` rule (@arogachev) +- New #649: Add `getFirstErrorMessagesIndexedByPath()` and `getFirstErrorMessagesIndexedByAttribute()` methods to + `Result` (@arogachev) ## 1.3.0 April 04, 2024