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

Commit ebc49a3

Browse files
authored
Merge pull request #13 from programmatordev/YAPV-13-add-custom-rule
Create custom rule
2 parents 7c70175 + c46e239 commit ebc49a3

File tree

8 files changed

+141
-21
lines changed

8 files changed

+141
-21
lines changed

src/ChainedValidatorInterface.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace ProgrammatorDev\YetAnotherPhpValidator;
44

55
use ProgrammatorDev\YetAnotherPhpValidator\Exception\ValidationException;
6+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\RuleInterface;
67

78
interface ChainedValidatorInterface
89
{
@@ -30,4 +31,6 @@ public function lessThanOrEqual(mixed $constraint, array $options = []): Chained
3031
public function notBlank(array $options = []): ChainedValidatorInterface;
3132

3233
public function range(mixed $minConstraint, mixed $maxConstraint, array $options = []): ChainedValidatorInterface;
34+
35+
public function rule(RuleInterface $constraint): ChainedValidatorInterface;
3336
}

src/Factory/Factory.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,23 @@
77

88
class Factory
99
{
10-
private string $namespace = 'ProgrammatorDev\\YetAnotherPhpValidator\\Rule';
10+
private array $namespaces = ['ProgrammatorDev\\YetAnotherPhpValidator\\Rule'];
1111

1212
/**
1313
* @throws RuleNotFoundException
1414
*/
1515
public function createRule(string $ruleName, array $arguments = []): RuleInterface
1616
{
17-
$className = \sprintf('%s\\%s', $this->namespace, \ucfirst($ruleName));
17+
foreach ($this->namespaces as $namespace) {
18+
$className = \sprintf('%s\\%s', $namespace, \ucfirst($ruleName));
1819

19-
if (!class_exists($className)) {
20-
throw new RuleNotFoundException(
21-
\sprintf('"%s" rule does not exist.', $ruleName)
22-
);
20+
if (class_exists($className)) {
21+
return new $className(...$arguments);
22+
}
2323
}
2424

25-
return new $className(...$arguments);
25+
throw new RuleNotFoundException(
26+
\sprintf('"%s" rule does not exist.', $ruleName)
27+
);
2628
}
2729
}

src/Rule/All.php

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,23 @@ public function assert(mixed $value, string $name): void
4141
);
4242
}
4343

44-
try {
45-
foreach ($value as $key => $input) {
46-
foreach ($this->constraints as $constraint) {
44+
foreach ($value as $key => $input) {
45+
foreach ($this->constraints as $constraint) {
46+
try {
4747
$constraint->assert($input, $name);
4848
}
49+
catch (ValidationException $exception) {
50+
throw new AllException(
51+
message: $this->options['message'],
52+
parameters: [
53+
'value' => $value,
54+
'name' => $name,
55+
'key' => $key,
56+
'message' => $exception->getMessage()
57+
]
58+
);
59+
}
4960
}
5061
}
51-
catch (ValidationException $exception) {
52-
throw new AllException(
53-
message: $this->options['message'],
54-
parameters: [
55-
'value' => $value,
56-
'name' => $name,
57-
'key' => $key,
58-
'message' => $exception->getMessage()
59-
]
60-
);
61-
}
6262
}
6363
}

src/Rule/Rule.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\YetAnotherPhpValidator\Rule;
4+
5+
class Rule extends AbstractRule implements RuleInterface
6+
{
7+
public function __construct(private readonly RuleInterface $constraint) {}
8+
9+
public function assert(mixed $value, string $name): void
10+
{
11+
$this->constraint->assert($value, $name);
12+
}
13+
}

src/StaticValidatorInterface.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace ProgrammatorDev\YetAnotherPhpValidator;
44

5+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\RuleInterface;
6+
57
interface StaticValidatorInterface
68
{
79
public static function all(array $constraints, array $options = null): ChainedValidatorInterface;
@@ -19,4 +21,6 @@ public static function lessThanOrEqual(mixed $constraint, array $options = []):
1921
public static function notBlank(array $options = []): ChainedValidatorInterface;
2022

2123
public static function range(mixed $minConstraint, mixed $maxConstraint, array $options = []): ChainedValidatorInterface;
24+
25+
public static function rule(RuleInterface $constraint): ChainedValidatorInterface;
2226
}

src/Test/Fixture/DummyRule.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\YetAnotherPhpValidator\Test\Fixture;
4+
5+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\UnexpectedValueException;
6+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\ValidationException;
7+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\AbstractRule;
8+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\RuleInterface;
9+
10+
class DummyRule extends AbstractRule implements RuleInterface
11+
{
12+
public function assert(mixed $value, string $name): void
13+
{
14+
if (!\is_bool($value)) {
15+
throw new UnexpectedValueException('Dummy unexpected value.');
16+
}
17+
18+
if ($value === false) {
19+
throw new ValidationException('Dummy exception.');
20+
}
21+
}
22+
}

tests/FactoryTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\YetAnotherPhpValidator\Test;
4+
5+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\RuleNotFoundException;
6+
use ProgrammatorDev\YetAnotherPhpValidator\Factory\Factory;
7+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\RuleInterface;
8+
9+
class FactoryTest extends AbstractTest
10+
{
11+
private Factory $factory;
12+
13+
protected function setUp(): void
14+
{
15+
parent::setUp();
16+
17+
$this->factory = new Factory();
18+
}
19+
20+
public function testFactoryCreateRuleFailure()
21+
{
22+
$this->expectException(RuleNotFoundException::class);
23+
$this->expectExceptionMessage('"invalidRule" rule does not exist.');
24+
$this->factory->createRule('invalidRule');
25+
}
26+
27+
public function testFactoryCreateRuleSuccess()
28+
{
29+
$this->assertInstanceOf(RuleInterface::class, $this->factory->createRule('notBlank'));
30+
}
31+
}

tests/RuleTest.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\YetAnotherPhpValidator\Test;
4+
5+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\ValidationException;
6+
use ProgrammatorDev\YetAnotherPhpValidator\Rule\Rule;
7+
use ProgrammatorDev\YetAnotherPhpValidator\Test\Fixture\DummyRule;
8+
use ProgrammatorDev\YetAnotherPhpValidator\Test\Util\TestRuleFailureConditionTrait;
9+
use ProgrammatorDev\YetAnotherPhpValidator\Test\Util\TestRuleSuccessConditionTrait;
10+
use ProgrammatorDev\YetAnotherPhpValidator\Test\Util\TestRuleUnexpectedValueTrait;
11+
12+
class RuleTest extends AbstractTest
13+
{
14+
use TestRuleUnexpectedValueTrait;
15+
use TestRuleFailureConditionTrait;
16+
use TestRuleSuccessConditionTrait;
17+
18+
public static function provideRuleUnexpectedValueData(): \Generator
19+
{
20+
yield 'invalid value type' => [
21+
new Rule(new DummyRule()),
22+
'invalid',
23+
'/Dummy unexpected value./'
24+
];
25+
}
26+
27+
public static function provideRuleFailureConditionData(): \Generator
28+
{
29+
yield 'invalid value' => [
30+
new Rule(new DummyRule()),
31+
false,
32+
ValidationException::class,
33+
'/Dummy exception./'
34+
];
35+
}
36+
37+
public static function provideRuleSuccessConditionData(): \Generator
38+
{
39+
yield 'valid value' => [
40+
new Rule(new DummyRule()),
41+
true
42+
];
43+
}
44+
45+
}

0 commit comments

Comments
 (0)