Skip to content

Commit fd7438f

Browse files
committed
Use a DI container to create instances of Validator
The `ValidatorDefaults` is cumbersome, and customising it can be annoying. Most projects use some sort of dependency injection container, and by integrating the creation of the `Validator` with the PSR-11, we allow users to easily customise how they create validators. Some tasks, like overwriting the `Translator`, become a bit more verbose, if the user is not already using a PSR-11 container, but I think that’s a good tradeoff.
1 parent bf9b970 commit fd7438f

14 files changed

+246
-448
lines changed

composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
},
2222
"require": {
2323
"php": ">=8.5",
24+
"php-di/php-di": "^7.1",
25+
"psr/container": "^2.0",
2426
"respect/stringifier": "^2.0.0",
2527
"symfony/polyfill-mbstring": "^1.33"
2628
},

docs/04-message-translation.md

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,26 @@
11
# Message translation
22

3-
You're able to translate message templates with Validation.
3+
We use dependency injection container to create Validators with all their dependencies. One of these dependencies is the `Translator`, which is responsible for translating validation messages.
44

5-
```php
6-
use Respect\Validation\Message\Translator\GettextTranslator;
7-
use Respect\Validation\ValidatorDefaults;
8-
9-
ValidatorDefaults::setTranslator(new GettextTranslator());
10-
```
11-
12-
After that, if you call the methods `getMessage()`, `getMessages()`, or `getFullMessage()` from the `ValidationException`, the messages will be translated. The example above will work if you have `gettext` properly configured.
5+
You can use different translators to translate validation messages into different languages, by overwriting the default container in `ContainerRegistry`, passing the translator you desire.
136

14-
For non-static usage, pass the translator directly to the `Validator` constructor:
7+
Luckily, the `ContainerRegistry` has method that creates a pre-configured container using [php-di/php-di](https://php-di.org/). That means you just need to overwrite that service.
158

169
```php
17-
use Respect\Validation\Factory;
18-
use Respect\Validation\Message\StandardFormatter;
10+
use Respect\Validation\ContainerRegistry;
11+
use Respect\Validation\Message\Translator;
1912
use Respect\Validation\Message\Translator\GettextTranslator;
20-
use Respect\Validation\Validator;
2113

22-
$translator = new GettextTranslator();
14+
$container = ContainerRegistry::createContainer(); // returns `DI\Container`
15+
$container->set(Translator::class, new GettextTranslator());
2316

24-
$validator = new Validator(new Factory(), new StandardFormatter(), $translator);
17+
ContainerRegistry::setContainer($container);
2518
```
2619

20+
After that, if you call the methods `getMessage()`, `getMessages()`, or `getFullMessage()` from the `ValidationException`, the messages will be translated. The example above will work if you have `gettext` properly configured.
21+
22+
The `ContainerRegistry` follows the [PSR-11](https://www.php-fig.org/psr/psr-11/) standard, so you can use any container that implements this standard.
23+
2724
## Supported translators
2825

2926
- `ArrayTranslator`: Translates messages using an array of messages.

library/CloneValidatorFactory.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/*
4+
* Copyright (c) Alexandre Gomes Gaigalas <[email protected]>
5+
* SPDX-License-Identifier: MIT
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Respect\Validation;
11+
12+
final readonly class CloneValidatorFactory implements ValidatorFactory
13+
{
14+
public function __construct(
15+
private Validator $validator,
16+
) {
17+
}
18+
19+
public function create(): Validator
20+
{
21+
return clone $this->validator;
22+
}
23+
}

library/ContainerRegistry.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
/*
4+
* Copyright (c) Alexandre Gomes Gaigalas <[email protected]>
5+
* SPDX-License-Identifier: MIT
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Respect\Validation;
11+
12+
use DI\Container;
13+
use Psr\Container\ContainerInterface;
14+
use Respect\Stringifier\Quoter;
15+
use Respect\Stringifier\Quoters\StandardQuoter;
16+
use Respect\Stringifier\Stringifier;
17+
use Respect\Validation\Message\Formatter\FirstResultStringFormatter;
18+
use Respect\Validation\Message\Formatter\NestedArrayFormatter;
19+
use Respect\Validation\Message\Formatter\NestedListStringFormatter;
20+
use Respect\Validation\Message\Formatter\TemplateResolver;
21+
use Respect\Validation\Message\InterpolationRenderer;
22+
use Respect\Validation\Message\Renderer;
23+
use Respect\Validation\Message\Translator;
24+
use Respect\Validation\Message\Translator\DummyTranslator;
25+
use Respect\Validation\Message\ValidationStringifier;
26+
use Respect\Validation\Transformers\Prefix;
27+
use Respect\Validation\Transformers\Transformer;
28+
29+
use function DI\autowire;
30+
use function DI\create;
31+
use function DI\factory;
32+
33+
final class ContainerRegistry
34+
{
35+
private static ContainerInterface|null $container = null;
36+
37+
public static function createContainer(): Container
38+
{
39+
return new Container([
40+
Transformer::class => create(Prefix::class),
41+
Factory::class => autowire(Factory::class),
42+
TemplateResolver::class => create(TemplateResolver::class),
43+
Quoter::class => create(StandardQuoter::class)->constructor(ValidationStringifier::MAXIMUM_LENGTH),
44+
Stringifier::class => create(ValidationStringifier::class),
45+
Translator::class => autowire(DummyTranslator::class),
46+
Renderer::class => autowire(InterpolationRenderer::class),
47+
ResultFilter::class => create(OnlyFailedChildrenResultFilter::class),
48+
'respect.validation.formatter.message' => autowire(FirstResultStringFormatter::class),
49+
'respect.validation.formatter.full_message' => autowire(NestedListStringFormatter::class),
50+
'respect.validation.formatter.messages' => autowire(NestedArrayFormatter::class),
51+
'respect.validation.ignored_backtrace_paths' => [__DIR__ . '/Validator.php'],
52+
Validator::class => factory(static fn(Container $container) => new Validator(
53+
$container->get(Factory::class),
54+
$container->get('respect.validation.formatter.message'),
55+
$container->get('respect.validation.formatter.full_message'),
56+
$container->get('respect.validation.formatter.messages'),
57+
$container->get(Translator::class),
58+
$container->get(ResultFilter::class),
59+
$container->get('respect.validation.ignored_backtrace_paths'),
60+
)),
61+
ValidatorFactory::class => autowire(CloneValidatorFactory::class),
62+
]);
63+
}
64+
65+
public static function getContainer(): ContainerInterface
66+
{
67+
if (!isset(self::$container)) {
68+
self::$container = self::createContainer();
69+
}
70+
71+
return self::$container;
72+
}
73+
74+
public static function setContainer(ContainerInterface $instance): void
75+
{
76+
self::$container = $instance;
77+
}
78+
}

library/Message/ValidationStringifier.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@
3838

3939
final readonly class ValidationStringifier implements Stringifier
4040
{
41+
public const int MAXIMUM_LENGTH = 120;
4142
private const int MAXIMUM_DEPTH = 3;
4243
private const int MAXIMUM_NUMBER_OF_ITEMS = 5;
4344
private const int MAXIMUM_NUMBER_OF_PROPERTIES = self::MAXIMUM_NUMBER_OF_ITEMS;
44-
private const int MAXIMUM_LENGTH = 120;
4545

4646
private Stringifier $stringifier;
4747

library/Validator.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,7 @@ public function __construct(
4949

5050
public static function create(Rule ...$rules): self
5151
{
52-
$validator = new self(
53-
ValidatorDefaults::getFactory(),
54-
ValidatorDefaults::getMainMessageFormatter(),
55-
ValidatorDefaults::getFullMessageFormatter(),
56-
ValidatorDefaults::getMessagesFormatter(),
57-
ValidatorDefaults::getTranslator(),
58-
new OnlyFailedChildrenResultFilter(),
59-
ValidatorDefaults::getIgnoredBacktracePaths(),
60-
);
52+
$validator = ContainerRegistry::getContainer()->get(ValidatorFactory::class)->create();
6153
$validator->rules = $rules;
6254

6355
return $validator;

library/ValidatorDefaults.php

Lines changed: 0 additions & 154 deletions
This file was deleted.

library/ValidatorFactory.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
/*
4+
* Copyright (c) Alexandre Gomes Gaigalas <[email protected]>
5+
* SPDX-License-Identifier: MIT
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Respect\Validation;
11+
12+
interface ValidatorFactory
13+
{
14+
public function create(): Validator;
15+
}

0 commit comments

Comments
 (0)