Skip to content

[RFC][Validator] Allow to set the translation domain or disable translations on a per-constraint level #59770

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: 7.3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ CHANGELOG
* Add support for multiple fields containing nested constraints in `Composite` constraints
* Add the `stopOnFirstError` option to the `Unique` constraint to validate all elements
* Add support for closures in the `When` constraint
* Add `translationDomain` to all constraints; setting it to `false` disables translations for the particular constraint

7.2
---
Expand Down
6 changes: 5 additions & 1 deletion src/Symfony/Component/Validator/Constraints/NotBlank.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class NotBlank extends Constraint
];

public string $message = 'This value should not be blank.';

public string|false|null $translationDomain = null;

public bool $allowNull = false;
/** @var callable|null */
public $normalizer;
Expand All @@ -41,7 +44,7 @@ class NotBlank extends Constraint
* @param string[]|null $groups
*/
#[HasNamedArguments]
public function __construct(?array $options = null, ?string $message = null, ?bool $allowNull = null, ?callable $normalizer = null, ?array $groups = null, mixed $payload = null)
public function __construct(?array $options = null, ?string $message = null, ?bool $allowNull = null, ?callable $normalizer = null, ?array $groups = null, mixed $payload = null, string|false|null $translationDomain = null)
{
if (\is_array($options)) {
trigger_deprecation('symfony/validator', '7.3', 'Passing an array of options to configure the "%s" constraint is deprecated, use named arguments instead.', static::class);
Expand All @@ -50,6 +53,7 @@ public function __construct(?array $options = null, ?string $message = null, ?bo
parent::__construct($options ?? [], $groups, $payload);

$this->message = $message ?? $this->message;
$this->translationDomain = $translationDomain ?? $this->translationDomain;
$this->allowNull = $allowNull ?? $this->allowNull;
$this->normalizer = $normalizer ?? $this->normalizer;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function validate(mixed $value, Constraint $constraint): void
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(NotBlank::IS_BLANK_ERROR)
->setOrDisableTranslationDomain($constraint->translationDomain)
->addViolation();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use PHPUnit\Framework\Constraint\IsNull;
use PHPUnit\Framework\Constraint\LogicalOr;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\GroupSequence;
Expand Down Expand Up @@ -61,6 +62,8 @@ abstract class ConstraintValidatorTestCase extends TestCase
protected Constraint $constraint;
protected ?string $defaultTimezone = null;

private TranslatorInterface&MockObject $translator;

private string $defaultLocale;
private array $expectedViolations;
private int $call;
Expand Down Expand Up @@ -122,14 +125,14 @@ protected function restoreDefaultTimezone()

protected function createContext()
{
$translator = $this->createMock(TranslatorInterface::class);
$translator->expects($this->any())->method('trans')->willReturnArgument(0);
$this->translator = $this->createMock(TranslatorInterface::class);
$this->translator->expects($this->any())->method('trans')->willReturnArgument(0);
$validator = $this->createMock(ValidatorInterface::class);
$validator->expects($this->any())
->method('validate')
->willReturnCallback(fn () => $this->expectedViolations[$this->call++] ?? new ConstraintViolationList());

$context = new ExecutionContext($validator, $this->root, $translator);
$context = new ExecutionContext($validator, $this->root, $this->translator);
$context->setGroup($this->group);
$context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
$context->setConstraint($this->constraint);
Expand Down Expand Up @@ -277,6 +280,14 @@ protected function expectViolationsAt(int $i, mixed $value, Constraint $constrai
return $context->getViolations();
}

protected function expectTranslationDomain(string $translationDomain)
{
$this->translator
->expects($this->atLeastOnce())
->method('trans')
->with($this->anything(), $this->anything(), $translationDomain, $this->anything());
}

protected function assertNoViolation()
{
$this->assertSame(0, $violationsCount = \count($this->context->getViolations()), \sprintf('0 violation expected. Got %u.', $violationsCount));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ public function testOutputWithClassArgument()
| | | | "allowNull" => false, |
| | | | "message" => "This value should not be blank.", |
| | | | "normalizer" => null, |
| | | | "payload" => null |
| | | | "payload" => null, |
| | | | "translationDomain" => null |
| | | | ] |
| email | property options | | [ |
| | | | "cascadeStrategy" => "None", |
Expand Down Expand Up @@ -111,7 +112,8 @@ public function testOutputWithPathArgument()
| | | | "allowNull" => false, |
| | | | "message" => "This value should not be blank.", |
| | | | "normalizer" => null, |
| | | | "payload" => null |
| | | | "payload" => null, |
| | | | "translationDomain" => null |
| | | | ] |
| email | property options | | [ |
| | | | "cascadeStrategy" => "None", |
Expand Down Expand Up @@ -153,7 +155,8 @@ public function testOutputWithPathArgument()
| | | | "allowNull" => false, |
| | | | "message" => "This value should not be blank.", |
| | | | "normalizer" => null, |
| | | | "payload" => null |
| | | | "payload" => null, |
| | | | "translationDomain" => null |
| | | | ] |
| email | property options | | [ |
| | | | "cascadeStrategy" => "None", |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ public function testNullIsInvalid()
->assertRaised();
}

public function testTranslationDomain()
{
$constraint = new NotBlank(translationDomain: 'my_domain');

$this->expectTranslationDomain('my_domain');

$this->validator->validate(null, $constraint);
}

public function testBlankIsInvalid()
{
$constraint = new NotBlank(message: 'myMessage');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ public function disableTranslation(): static
return $this;
}

/**
* @return $this
*/
public function setOrDisableTranslationDomain(string|false|null $translationDomain): static
{
$this->translationDomain = $translationDomain;

return $this;
}

public function setInvalidValue(mixed $invalidValue): static
{
$this->invalidValue = $invalidValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
* execution context.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @method $this setOrDisableTranslationDomain(string|false|null $translationDomain)
*/
interface ConstraintViolationBuilderInterface
{
Expand Down
Loading