Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 8cd8232

Browse files
committed
feat: added Count rule
1 parent c010641 commit 8cd8232

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed

src/Exception/CountException.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Exception;
4+
5+
class CountException extends ValidationException {}

src/Rule/Count.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Rule;
4+
5+
use ProgrammatorDev\Validator\Exception\CountException;
6+
use ProgrammatorDev\Validator\Exception\UnexpectedTypeException;
7+
use ProgrammatorDev\Validator\Exception\UnexpectedValueException;
8+
9+
class Count extends AbstractRule implements RuleInterface
10+
{
11+
private string $minMessage = 'The {{ name }} value should contain {{ min }} elements or more, {{ numElements }} elements given.';
12+
private string $maxMessage = 'The {{ name }} value should contain {{ max }} elements or less, {{ numElements }} elements given.';
13+
private string $exactMessage = 'The {{ name }} value should contain exactly {{ numElements }} elements, {{ numElements }} elements given.';
14+
15+
public function __construct(
16+
private readonly ?int $min = null,
17+
private readonly ?int $max = null,
18+
?string $minMessage = null,
19+
?string $maxMessage = null,
20+
?string $exactMessage = null
21+
)
22+
{
23+
$this->minMessage = $minMessage ?? $this->minMessage;
24+
$this->maxMessage = $maxMessage ?? $this->maxMessage;
25+
$this->exactMessage = $exactMessage ?? $this->exactMessage;
26+
}
27+
28+
public function assert(mixed $value, ?string $name = null): void
29+
{
30+
if ($this->min === null && $this->max === null) {
31+
throw new UnexpectedValueException('At least one of the options "min" or "max" must be given.');
32+
}
33+
34+
if (!\is_countable($value)) {
35+
throw new UnexpectedTypeException('array|\Countable', get_debug_type($value));
36+
}
37+
38+
$numElements = \count($value);
39+
40+
if ($this->min !== null && $numElements < $this->min) {
41+
$message = $this->min === $this->max ? $this->exactMessage : $this->minMessage;
42+
43+
throw new CountException(
44+
message: $message,
45+
parameters: [
46+
'value' => $value,
47+
'name' => $name,
48+
'min' => $this->min,
49+
'max' => $this->max,
50+
'numElements' => $numElements
51+
]
52+
);
53+
}
54+
55+
if ($this->max !== null && $numElements > $this->max) {
56+
$message = $this->min === $this->max ? $this->exactMessage : $this->maxMessage;
57+
58+
throw new CountException(
59+
message: $message,
60+
parameters: [
61+
'value' => $value,
62+
'name' => $name,
63+
'min' => $this->min,
64+
'max' => $this->max,
65+
'numElements' => $numElements
66+
]
67+
);
68+
}
69+
}
70+
}

tests/CountTest.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Test;
4+
5+
use ProgrammatorDev\Validator\Exception\CountException;
6+
use ProgrammatorDev\Validator\Rule\Count;
7+
use ProgrammatorDev\Validator\Test\Util\TestRuleFailureConditionTrait;
8+
use ProgrammatorDev\Validator\Test\Util\TestRuleMessageOptionTrait;
9+
use ProgrammatorDev\Validator\Test\Util\TestRuleSuccessConditionTrait;
10+
use ProgrammatorDev\Validator\Test\Util\TestRuleUnexpectedValueTrait;
11+
12+
class CountTest extends AbstractTest
13+
{
14+
use TestRuleUnexpectedValueTrait;
15+
use TestRuleFailureConditionTrait;
16+
use TestRuleSuccessConditionTrait;
17+
use TestRuleMessageOptionTrait;
18+
19+
public static function provideRuleUnexpectedValueData(): \Generator
20+
{
21+
$missingOptionsMessage = '/At least one of the options "min" or "max" must be given./';
22+
$invalidTypeMessage = '/Expected value of type "array|\Countable", "(.*)" given./';
23+
24+
yield 'missing options' => [new Count(), [1, 2, 3], $missingOptionsMessage];
25+
yield 'invalid type value' => [new Count(min: 5, max: 10), 1, $invalidTypeMessage];
26+
}
27+
28+
public static function provideRuleFailureConditionData(): \Generator
29+
{
30+
$value = [1, 2, 3, 4, 5];
31+
$exception = CountException::class;
32+
$minMessage = '/The (.*) value should contain (.*) elements or more, (.*) elements given./';
33+
$maxMessage = '/The (.*) value should contain (.*) elements or less, (.*) elements given./';
34+
$exactMessage = '/The (.*) value should contain exactly (.*) elements, (.*) elements given./';
35+
36+
yield 'min constraint' => [new Count(min: 10), $value, $exception, $minMessage];
37+
yield 'max constraint' => [new Count(max: 2), $value, $exception, $maxMessage];
38+
yield 'min and max constraint' => [new Count(min: 10, max: 20), $value, $exception, $minMessage];
39+
yield 'exact constraint' => [new Count(min: 2, max: 2), $value, $exception, $exactMessage];
40+
}
41+
42+
public static function provideRuleSuccessConditionData(): \Generator
43+
{
44+
$value = [1, 2, 3, 4, 5];
45+
46+
yield 'min constraint' => [new Count(min: 5), $value];
47+
yield 'max constraint' => [new Count(max: 5), $value];
48+
yield 'min and max constraint' => [new Count(min: 4, max: 6), $value];
49+
yield 'exact constraint' => [new Count(min: 5, max: 5), $value];
50+
}
51+
52+
public static function provideRuleMessageOptionData(): \Generator
53+
{
54+
$value = [1, 2, 3, 4, 5];
55+
56+
yield 'min message' => [
57+
new Count(
58+
min: 10,
59+
minMessage: 'The {{ name }} value should have at least {{ min }} elements.'
60+
),
61+
$value,
62+
'The test value should have at least 10 elements.'
63+
];
64+
yield 'max message' => [
65+
new Count(
66+
max: 2,
67+
maxMessage: 'The {{ name }} value should have at most {{ max }} elements.'
68+
),
69+
$value,
70+
'The test value should have at most 2 elements.'
71+
];
72+
yield 'exact message' => [
73+
new Count(
74+
min: 2,
75+
max: 2,
76+
exactMessage: 'The {{ name }} value should have exactly {{ min }} elements.'
77+
),
78+
$value,
79+
'The test value should have exactly 2 elements.'
80+
];
81+
}
82+
}

0 commit comments

Comments
 (0)