Skip to content

Commit 9557c71

Browse files
committed
Add ConstraintViolationBuilderInterface methods: fromViolation(), setPath(), getViolation()
1 parent e9f91a6 commit 9557c71

File tree

3 files changed

+96
-27
lines changed

3 files changed

+96
-27
lines changed

src/Symfony/Component/Validator/Tests/Violation/ConstraintViolationBuilderTest.php

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
use Symfony\Component\Translation\IdentityTranslator;
1616
use Symfony\Component\Validator\Constraints\Valid;
1717
use Symfony\Component\Validator\ConstraintViolation;
18+
use Symfony\Component\Validator\ConstraintViolationInterface;
1819
use Symfony\Component\Validator\ConstraintViolationList;
20+
use Symfony\Component\Validator\Util\PropertyPath;
1921
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
2022
use Symfony\Contracts\Translation\TranslatorInterface;
2123

@@ -42,7 +44,7 @@ public function testAddViolation()
4244
{
4345
$this->builder->addViolation();
4446

45-
$this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data', 'foo', null, null, new Valid()));
47+
$this->assertBuiltViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data', 'foo', null, null, new Valid()));
4648
}
4749

4850
public function testAppendPropertyPath()
@@ -51,7 +53,7 @@ public function testAppendPropertyPath()
5153
->atPath('foo')
5254
->addViolation();
5355

54-
$this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data.foo', 'foo', null, null, new Valid()));
56+
$this->assertBuiltViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data.foo', 'foo', null, null, new Valid()));
5557
}
5658

5759
public function testAppendMultiplePropertyPaths()
@@ -61,7 +63,7 @@ public function testAppendMultiplePropertyPaths()
6163
->atPath('bar')
6264
->addViolation();
6365

64-
$this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data.foo.bar', 'foo', null, null, new Valid()));
66+
$this->assertBuiltViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data.foo.bar', 'foo', null, null, new Valid()));
6567
}
6668

6769
public function testCodeCanBeSet()
@@ -70,7 +72,7 @@ public function testCodeCanBeSet()
7072
->setCode('5')
7173
->addViolation();
7274

73-
$this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data', 'foo', null, '5', new Valid()));
75+
$this->assertBuiltViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data', 'foo', null, '5', new Valid()));
7476
}
7577

7678
public function testCauseCanBeSet()
@@ -81,7 +83,7 @@ public function testCauseCanBeSet()
8183
->setCause($cause)
8284
->addViolation();
8385

84-
$this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data', 'foo', null, null, new Valid(), $cause));
86+
$this->assertBuiltViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'data', 'foo', null, null, new Valid(), $cause));
8587
}
8688

8789
public function testTranslationDomainFalse()
@@ -96,12 +98,31 @@ public function testTranslationDomainFalse()
9698
$builder->addViolation();
9799
}
98100

99-
private function assertViolationEquals(ConstraintViolation $expectedViolation)
101+
public function testBuildViolationFromExistingViolation()
102+
{
103+
$originalViolation = $this->builder->getViolation();
104+
105+
$violation = ConstraintViolationBuilder::fromViolation($originalViolation)
106+
->setPath(PropertyPath::append('top', $originalViolation->getPropertyPath()))
107+
->setCause($cause = new \LogicException())
108+
->getViolation();
109+
110+
$this->assertCount(0, $this->violations);
111+
112+
$this->assertViolationEquals(new ConstraintViolation($this->messageTemplate, $this->messageTemplate, [], $this->root, 'top.data', 'foo', null, null, new Valid(), $cause), $violation);
113+
}
114+
115+
private function assertBuiltViolationEquals(ConstraintViolation $expectedViolation): void
100116
{
101117
$this->assertCount(1, $this->violations);
102118

103119
$violation = $this->violations->get(0);
104120

121+
$this->assertViolationEquals($expectedViolation, $violation);
122+
}
123+
124+
private function assertViolationEquals(ConstraintViolation $expectedViolation, ConstraintViolationInterface $violation): void
125+
{
105126
$this->assertSame($expectedViolation->getMessage(), $violation->getMessage());
106127
$this->assertSame($expectedViolation->getMessageTemplate(), $violation->getMessageTemplate());
107128
$this->assertSame($expectedViolation->getParameters(), $violation->getParameters());

src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313

1414
use Symfony\Component\Validator\Constraint;
1515
use Symfony\Component\Validator\ConstraintViolation;
16-
use Symfony\Component\Validator\ConstraintViolationList;
16+
use Symfony\Component\Validator\ConstraintViolationInterface;
17+
use Symfony\Component\Validator\ConstraintViolationListInterface;
1718
use Symfony\Component\Validator\Util\PropertyPath;
1819
use Symfony\Contracts\Translation\TranslatorInterface;
1920

@@ -27,24 +28,52 @@
2728
class ConstraintViolationBuilder implements ConstraintViolationBuilderInterface
2829
{
2930
private string $propertyPath;
31+
private ?string $message = null;
3032
private ?int $plural = null;
3133
private ?string $code = null;
3234
private mixed $cause = null;
3335

3436
public function __construct(
35-
private ConstraintViolationList $violations,
37+
private ?ConstraintViolationListInterface $violations,
3638
private ?Constraint $constraint,
37-
private string|\Stringable $message,
39+
private string|\Stringable $messageTemplate,
3840
private array $parameters,
3941
private mixed $root,
4042
?string $propertyPath,
4143
private mixed $invalidValue,
42-
private TranslatorInterface $translator,
44+
private TranslatorInterface|null $translator = null,
4345
private string|false|null $translationDomain = null,
4446
) {
4547
$this->propertyPath = $propertyPath ?? '';
4648
}
4749

50+
public static function fromViolation(ConstraintViolationInterface $violation): static
51+
{
52+
$builder = new self(
53+
null,
54+
$violation->getConstraint(),
55+
$violation->getMessageTemplate(),
56+
$violation->getParameters(),
57+
$violation->getRoot(),
58+
$violation->getPropertyPath(),
59+
$violation->getInvalidValue(),
60+
);
61+
62+
$builder->message = $violation->getMessage();
63+
64+
return $builder
65+
->setPlural($violation->getPlural())
66+
->setCode($violation->getCode())
67+
->setCause($violation->getCause());
68+
}
69+
70+
public function setPath(string $path): static
71+
{
72+
$this->propertyPath = $path;
73+
74+
return $this;
75+
}
76+
4877
public function atPath(string $path): static
4978
{
5079
$this->propertyPath = PropertyPath::append($this->propertyPath, $path);
@@ -79,6 +108,7 @@ public function setTranslationDomain(string $translationDomain): static
79108
public function disableTranslation(): static
80109
{
81110
$this->translationDomain = false;
111+
$this->translator = null;
82112

83113
return $this;
84114
}
@@ -90,7 +120,7 @@ public function setInvalidValue(mixed $invalidValue): static
90120
return $this;
91121
}
92122

93-
public function setPlural(int $number): static
123+
public function setPlural(?int $number): static
94124
{
95125
$this->plural = $number;
96126

@@ -113,20 +143,18 @@ public function setCause(mixed $cause): static
113143

114144
public function addViolation(): void
115145
{
116-
$parameters = null === $this->plural ? $this->parameters : (['%count%' => $this->plural] + $this->parameters);
117-
if (false === $this->translationDomain) {
118-
$translatedMessage = strtr($this->message, $parameters);
119-
} else {
120-
$translatedMessage = $this->translator->trans(
121-
$this->message,
122-
$parameters,
123-
$this->translationDomain
124-
);
146+
if (null === $this->violations) {
147+
throw new \LogicException('Cannot add a violation without an execution context.');
125148
}
126149

127-
$this->violations->add(new ConstraintViolation(
128-
$translatedMessage,
129-
$this->message,
150+
$this->violations->add($this->getViolation());
151+
}
152+
153+
public function getViolation(): ConstraintViolationInterface
154+
{
155+
return new ConstraintViolation(
156+
$this->message ??= $this->translateMessage(),
157+
$this->messageTemplate,
130158
$this->parameters,
131159
$this->root,
132160
$this->propertyPath,
@@ -135,6 +163,21 @@ public function addViolation(): void
135163
$this->code,
136164
$this->constraint,
137165
$this->cause
138-
));
166+
);
167+
}
168+
169+
private function translateMessage(): string
170+
{
171+
$parameters = null === $this->plural ? $this->parameters : (['%count%' => $this->plural] + $this->parameters);
172+
173+
if (null === $this->translator || false === $this->translationDomain) {
174+
return strtr($this->messageTemplate, $parameters);
175+
}
176+
177+
return $this->translator->trans(
178+
$this->messageTemplate,
179+
$parameters,
180+
$this->translationDomain
181+
);
139182
}
140183
}

src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,20 @@
1111

1212
namespace Symfony\Component\Validator\Violation;
1313

14+
use Symfony\Component\Validator\ConstraintViolationInterface;
15+
1416
/**
15-
* Builds {@link \Symfony\Component\Validator\ConstraintViolationInterface}
17+
* Builds {@link ConstraintViolationInterface}
1618
* objects.
1719
*
1820
* Use the various methods on this interface to configure the built violation.
1921
* Finally, call {@link addViolation()} to add the violation to the current
2022
* execution context.
2123
*
2224
* @author Bernhard Schussek <bschussek@gmail.com>
25+
*
26+
* @method ConstraintViolationInterface getViolation()
27+
* @method $this setPath(string $path)
2328
*/
2429
interface ConstraintViolationBuilderInterface
2530
{
@@ -84,13 +89,13 @@ public function setInvalidValue(mixed $invalidValue): static;
8489
* Sets the number which determines how the plural form of the violation
8590
* message is chosen when it is translated.
8691
*
87-
* @param int $number The number for determining the plural form
92+
* @param int|null $number The number for determining the plural form
8893
*
8994
* @return $this
9095
*
9196
* @see \Symfony\Contracts\Translation\TranslatorInterface::trans()
9297
*/
93-
public function setPlural(int $number): static;
98+
public function setPlural(?int $number): static;
9499

95100
/**
96101
* Sets the violation code.

0 commit comments

Comments
 (0)