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

Commit 1719767

Browse files
committed
feat: added Optional rule
1 parent 120ee88 commit 1719767

File tree

6 files changed

+112
-8
lines changed

6 files changed

+112
-8
lines changed

src/ChainedValidatorInterface.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ public function notBlank(
9797
?string $message = null
9898
): ChainedValidatorInterface&Validator;
9999

100+
public function optional(
101+
Validator $validator
102+
): ChainedValidatorInterface&Validator;
103+
100104
public function passwordStrength(
101105
string $minStrength = 'medium',
102106
?string $message = null

src/Rule/Collection.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ public function assert(mixed $value, ?string $name = null): void
4545
}
4646

4747
foreach ($this->fields as $field => $validator) {
48-
if (!isset($value[$field])) {
48+
// find if validation is optional
49+
$isOptional = $validator->getRules()[0] instanceof Optional;
50+
51+
if (!isset($value[$field]) && !$isOptional) {
4952
throw new CollectionException(
5053
message: $this->missingFieldsMessage,
5154
parameters: [
@@ -55,6 +58,11 @@ public function assert(mixed $value, ?string $name = null): void
5558
);
5659
}
5760

61+
// if value is not set but field is optional
62+
if (!isset($value[$field]) && $isOptional) {
63+
$value[$field] = null;
64+
}
65+
5866
try {
5967
$validator->assert($value[$field], \sprintf('"%s"', $field));
6068
}

src/Rule/Optional.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Rule;
4+
5+
use ProgrammatorDev\Validator\Validator;
6+
7+
class Optional extends AbstractRule implements RuleInterface
8+
{
9+
public function __construct(
10+
private readonly Validator $validator
11+
) {}
12+
13+
public function assert(mixed $value, ?string $name = null): void
14+
{
15+
// validate only if value is not null
16+
if ($value === null) {
17+
return;
18+
}
19+
20+
$this->validator->assert($value, $name);
21+
}
22+
}

src/StaticValidatorInterface.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ public static function notBlank(
9696
?string $message = null
9797
): ChainedValidatorInterface&Validator;
9898

99+
public static function optional(
100+
Validator $validator
101+
): ChainedValidatorInterface&Validator;
102+
99103
public static function passwordStrength(
100104
string $minStrength = 'medium',
101105
?string $message = null

tests/CollectionTest.php

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,15 @@ public static function provideRuleUnexpectedValueData(): \Generator
3737
public static function provideRuleFailureConditionData(): \Generator
3838
{
3939
$exception = CollectionException::class;
40+
$notBlankMessage = '/The "(.*)" value should not be blank, "" given\./';
4041
$extraFieldsMessage = '/The (.*) field is not allowed\./';
4142
$missingFieldsMessage = '/The (.*) field is missing\./';
4243

4344
yield 'invalid field' => [
4445
new Collection(fields: ['field' => Validator::notBlank()]),
4546
['field' => ''],
4647
$exception,
47-
'/The "(.*)" value should not be blank, "" given\./'
48+
$notBlankMessage
4849
];
4950
yield 'extra fields' => [
5051
new Collection(fields: ['field' => Validator::notBlank()]),
@@ -53,16 +54,25 @@ public static function provideRuleFailureConditionData(): \Generator
5354
$extraFieldsMessage
5455
];
5556
yield 'missing fields' => [
56-
new Collection(
57-
fields: [
58-
'field1' => Validator::notBlank(),
59-
'field2' => Validator::notBlank()
60-
]
61-
),
57+
new Collection(fields: [
58+
'field1' => Validator::notBlank(),
59+
'field2' => Validator::notBlank()
60+
]),
6261
['field1' => 'value1'],
6362
$exception,
6463
$missingFieldsMessage
6564
];
65+
yield 'optional' => [
66+
new Collection(fields: [
67+
'field' => Validator::notBlank(),
68+
'optional' => Validator::optional(
69+
Validator::notBlank()
70+
)
71+
]),
72+
['field' => 'value', 'optional' => ''],
73+
$exception,
74+
$notBlankMessage
75+
];
6676
}
6777

6878
public static function provideRuleSuccessConditionData(): \Generator
@@ -82,6 +92,24 @@ public static function provideRuleSuccessConditionData(): \Generator
8292
),
8393
['field' => 'value', 'extrafield' => 'extravalue']
8494
];
95+
yield 'optional' => [
96+
new Collection(fields: [
97+
'field' => Validator::notBlank(),
98+
'optional' => Validator::optional(
99+
Validator::notBlank()
100+
)
101+
]),
102+
['field' => 'value']
103+
];
104+
yield 'optional null' => [
105+
new Collection(fields: [
106+
'field' => Validator::notBlank(),
107+
'optional' => Validator::optional(
108+
Validator::notBlank()
109+
)
110+
]),
111+
['field' => 'value', 'optional' => null]
112+
];
85113
}
86114

87115
public static function provideRuleMessageOptionData(): \Generator

tests/OptionalTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Test;
4+
5+
use ProgrammatorDev\Validator\Exception\NotBlankException;
6+
use ProgrammatorDev\Validator\Rule\NotBlank;
7+
use ProgrammatorDev\Validator\Rule\Optional;
8+
use ProgrammatorDev\Validator\Test\Util\TestRuleFailureConditionTrait;
9+
use ProgrammatorDev\Validator\Test\Util\TestRuleSuccessConditionTrait;
10+
use ProgrammatorDev\Validator\Validator;
11+
12+
class OptionalTest extends AbstractTest
13+
{
14+
use TestRuleFailureConditionTrait;
15+
use TestRuleSuccessConditionTrait;
16+
17+
public static function provideRuleFailureConditionData(): \Generator
18+
{
19+
yield 'default' => [
20+
new Optional(
21+
new Validator(new NotBlank())
22+
),
23+
'',
24+
NotBlankException::class,
25+
'/The (.*) value should not be blank, (.*) given\./'
26+
];
27+
}
28+
29+
public static function provideRuleSuccessConditionData(): \Generator
30+
{
31+
yield 'default' => [
32+
new Optional(
33+
new Validator(new NotBlank())
34+
),
35+
null
36+
];
37+
}
38+
}

0 commit comments

Comments
 (0)