Skip to content

Commit

Permalink
Merge pull request #1 from przemyslaw-przylucki/feat/multiple-validat…
Browse files Browse the repository at this point in the history
…ion-messages

[RFC] Multiple validation messages
  • Loading branch information
brendt authored May 31, 2024
2 parents 05f000d + 2ddde79 commit 83288d8
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 22 deletions.
24 changes: 24 additions & 0 deletions src/Support/LanguageHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Tempest\Support;

final class LanguageHelper
{
/**
* @param string[] $parts
*
* @return string
*/
public static function join(array $parts): string
{
$last = array_pop($parts);

if ($parts) {
return implode(', ', $parts) . ' ' . 'and' . ' ' . $last;
}

return $last;
}
}
4 changes: 3 additions & 1 deletion src/Validation/Exceptions/ValidationException.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace Tempest\Validation\Exceptions;

use Exception;
use Tempest\Support\ArrayHelper;
use Tempest\Support\LanguageHelper;
use Tempest\Validation\Rule;

final class ValidationException extends Exception
Expand All @@ -20,7 +22,7 @@ public function __construct(object $object, array $failingRules)
foreach ($failingRules as $field => $failingRulesForField) {
/** @var Rule $failingRuleForField */
foreach ($failingRulesForField as $failingRuleForField) {
$messages[$field][] = $failingRuleForField->message();
$messages[$field][] = LanguageHelper::join(ArrayHelper::wrap($failingRuleForField->message()));
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/Validation/Rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ interface Rule
{
public function isValid(mixed $value): bool;

public function message(): string;
/**
* @return string|string[]
*/
public function message(): string|array;
}
19 changes: 6 additions & 13 deletions src/Validation/Rules/Password.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ public function isValid(mixed $value): bool
return true;
}

public function message(): string
/**
* @return string[]
*/
public function message(): array
{
$messages = ["at least {$this->min} characters"];
$messages = ["Value should contain at least {$this->min} characters"];

if ($this->mixedCase) {
$messages[] = 'at least one uppercase and one lowercase letter';
Expand All @@ -68,16 +71,6 @@ public function message(): string
$messages[] = 'at least one symbol';
}

return 'Value should contain ' . $this->natural_language_join($messages);
}

private function natural_language_join(array $list)
{
$last = array_pop($list);
if ($list) {
return implode(', ', $list) . ' ' . 'and' . ' ' . $last;
}

return $last;
return $messages;
}
}
22 changes: 15 additions & 7 deletions tests/Unit/Validation/Rules/PasswordTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,32 @@ public function test_symbols()
public function test_message()
{
$rule = new Password();
$this->assertSame('Value should contain at least 12 characters', $rule->message());
$this->assertSame('Value should contain at least 12 characters', $rule->message()[0]);

$rule = new Password(min: 4);
$this->assertSame('Value should contain at least 4 characters', $rule->message());
$this->assertSame('Value should contain at least 4 characters', $rule->message()[0]);

$rule = new Password(mixedCase: true);
$this->assertSame('Value should contain at least 12 characters and at least one uppercase and one lowercase letter', $rule->message());
$this->assertSame('Value should contain at least 12 characters', $rule->message()[0]);
$this->assertSame('at least one uppercase and one lowercase letter', $rule->message()[1]);

$rule = new Password(letters: true);
$this->assertSame('Value should contain at least 12 characters and at least one letter', $rule->message());
$this->assertSame('Value should contain at least 12 characters', $rule->message()[0]);
$this->assertSame('at least one letter', $rule->message()[1]);

$rule = new Password(numbers: true);
$this->assertSame('Value should contain at least 12 characters and at least one number', $rule->message());
$this->assertSame('Value should contain at least 12 characters', $rule->message()[0]);
$this->assertSame('at least one number', $rule->message()[1]);

$rule = new Password(symbols: true);
$this->assertSame('Value should contain at least 12 characters and at least one symbol', $rule->message());
$this->assertSame('Value should contain at least 12 characters', $rule->message()[0]);
$this->assertSame('at least one symbol', $rule->message()[1]);

$rule = new Password(min: 4, mixedCase: true, letters: true, numbers: true, symbols: true);
$this->assertSame('Value should contain at least 4 characters, at least one uppercase and one lowercase letter, at least one number, at least one letter and at least one symbol', $rule->message());
$this->assertSame('Value should contain at least 4 characters', $rule->message()[0]);
$this->assertSame('at least one uppercase and one lowercase letter', $rule->message()[1]);
$this->assertSame('at least one number', $rule->message()[2]);
$this->assertSame('at least one letter', $rule->message()[3]);
$this->assertSame('at least one symbol', $rule->message()[4]);
}
}
78 changes: 78 additions & 0 deletions tests/Unit/Validation/ValidationExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

namespace Tests\Tempest\Unit\Validation;

use PHPUnit\Framework\TestCase;
use stdClass;
use Tempest\Validation\Exceptions\ValidationException;
use Tempest\Validation\Rule;

/**
* @internal
* @small
*/
final class ValidationExceptionTest extends TestCase
{
public function test_exception_message(): void
{
$this->expectException(ValidationException::class);

$this->expectExceptionMessage('Value should be a valid email address');

throw new ValidationException(new stdClass(), [
'email' => [
new class () implements Rule {
public function isValid(mixed $value): bool
{
return false;
}

public function message(): string|array
{
return 'Value should be a valid email address';
}
},
],
]);
}

public function test_exception_message_with_multiple_messages(): void
{
$this->expectException(ValidationException::class);

$this->expectExceptionMessage('Value should be a valid email address');
$this->expectExceptionMessage("Value should praise tempest, old gods from the past and the new gods from the future");

throw new ValidationException(new stdClass(), [
'email' => [
new class () implements Rule {
public function isValid(mixed $value): bool
{
return false;
}

public function message(): string|array
{
return 'Value should be a valid email address';
}
},
new class () implements Rule {
public function isValid(mixed $value): bool
{
return false;
}

public function message(): string|array
{
return [
'Value should praise tempest',
'old gods from the past',
'the new gods from the future',
];
}
}],
]);
}
}

0 comments on commit 83288d8

Please sign in to comment.