Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods for getting first error messages to Result #697

Merged
merged 11 commits into from
Apr 12, 2024
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
48 changes: 48 additions & 0 deletions src/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,30 @@ 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<string, string>
*/
public function getFirstErrorMessagesIndexedByPath(string $separator = '.', ?string $escape = '.'): array
{
$errors = [];
foreach ($this->errors as $error) {
$stringValuePath = implode($separator, $error->getValuePath($escape));
$errors[$stringValuePath] ??= $error->getMessage();
}

return $errors;
}

/**
* Get arrays of error messages indexed by attribute name.
*
Expand All @@ -118,6 +142,30 @@ 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<string, string>
*/
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.');
}

$errors[$key] ??= $error->getMessage();
}

return $errors;
}

/**
* Get an array of error objects for the attribute specified.
*
Expand Down
52 changes: 39 additions & 13 deletions tests/ResultTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
[
Expand All @@ -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();
Expand All @@ -100,16 +106,36 @@ 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();
}

public function testGetFirstErrorMessagesIndexedByAttribute(): void
{
$this->assertSame(
[
'attribute2' => 'error2.1',
'' => 'error3.1',
'attribute4' => 'error4.1',
],
$this->createAttributeErrorResult()->getFirstErrorMessagesIndexedByAttribute(),
);
}

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();
Expand Down Expand Up @@ -269,7 +295,7 @@ private function createErrorResult(): Result
$result = new Result();
$result
->addError('error1')
->addError('error2', [], ['path', 2]);
->addError('error2', valuePath: ['path', 2]);

return $result;
}
Expand Down
Loading