8000 [Validator] Add support for the `otherwise` option in the `When` cons… · symfony/symfony@5439efb · GitHub
[go: up one dir, main page]

Skip to content

Commit 5439efb

Browse files
[Validator] Add support for the otherwise option in the When constraint
1 parent 04551ad commit 5439efb

File tree

8 files changed

+200
-49
lines changed

8 files changed

+200
-49
lines changed

src/Symfony/Component/Validator/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ CHANGELOG
5656
```
5757
* Add support for ratio checks for SVG files to the `Image` constraint
5858
* Add the `Slug` constraint
59+
* Add support for the `otherwise` option in the `When` constraint
60+
* Add support for multiple fields containing nested constraints in `Composite` constraints
5961

6062
7.2
6163
---

src/Symfony/Component/Validator/Constraints/Composite.php

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -55,57 +55,63 @@ public function __construct(mixed $options = null, ?array $groups = null, mixed
5555

5656
$this->initializeNestedConstraints();
5757

58-
/* @var Constraint[] $nestedConstraints */
59-
$compositeOption = $this->getCompositeOption();
60-
$nestedConstraints = $this->$compositeOption;
61-
62-
if (!\is_array($nestedConstraints)) {
63-
$nestedConstraints = [$nestedConstraints];
58+
$compositeOptions = $this->getCompositeOption();
59+
if (!\is_array($compositeOptions)) {
60+
$compositeOptions = [$compositeOptions];
6461
}
6562

66-
foreach ($nestedConstraints as $constraint) {
67-
if (!$constraint instanceof Constraint) {
68-
if (\is_object($constraint)) {
69-
$constraint = $constraint::class;
70-
}
63+
foreach ($compositeOptions as $compositeOption) {
64+
/* @var Constraint[] $nestedConstraints */
65+
$nestedConstraints = $this->$compositeOption;
7166

72-
throw new ConstraintDefinitionException(\sprintf('The value "%s" is not an instance of Constraint in constraint "%s".', $constraint, static::class));
67+
if (!\is_array($nestedConstraints)) {
68+
$nestedConstraints = [$nestedConstraints];
7369
}
7470

75-
if ($constraint instanceof Valid) {
76-
throw new ConstraintDefinitionException(\sprintf('The constraint Valid cannot be nested inside constraint "%s". You can only declare the Valid constraint directly on a field or method.', static::class));
77-
}
78-
}
71+
foreach ($nestedConstraints as $constraint) {
72+
if (!$constraint instanceof Constraint) {
73+
if (\is_object($constraint)) {
74+
$constraint = $constraint::class;
75+
}
7976

80-
if (!isset(((array) $this)['groups'])) {
81-
$mergedGroups = [];
77+
throw new ConstraintDefinitionException(\sprintf('The value "%s" is not an instance of Constraint in constraint "%s".', $constraint, static::class));
78+
}
8279

83-
foreach ($nestedConstraints as $constraint) {
84-
foreach ($constraint->groups as $group) {
85-
$mergedGroups[$group] = true;
80+
if ($constraint instanceof Valid) {
81+
throw new ConstraintDefinitionException(\sprintf('The constraint Valid cannot be nested inside constraint "%s". You can only declare the Valid constraint directly on a field or method.', static::class));
8682
}
8783
}
8884

89-
// prevent empty composite constraint to have empty groups
90-
$this->groups = array_keys($mergedGroups) ?: [self::DEFAULT_GROUP];
91-
$this->$compositeOption = $nestedConstraints;
85+
if (!isset(((array) $this)['groups'])) {
86+
$mergedGroups = [];
9287

93-
return;
94-
}
88+
foreach ($nestedConstraints as $constraint) {
89+
foreach ($constraint->groups as $group) {
90+
$mergedGroups[$group] = true;
91+
}
92+
}
93+
94+
// prevent empty composite constraint to have empty groups
95+
$this->groups = array_keys($mergedGroups) ?: [self::DEFAULT_GROUP];
96+
$this->$compositeOption = $nestedConstraints;
9597

96-
foreach ($nestedConstraints as $constraint) {
97-
if (isset(((array) $constraint)['groups'])) {
98-
$excessGroups = array_diff($constraint->groups, $this->groups);
98+
continue;
99+
}
99100

100-
if (\count($excessGroups) > 0) {
101-
throw new ConstraintDefinitionException(\sprintf('The group(s) "%s" passed to the constraint "%s" should also be passed to its containing constraint "%s".', implode('", "', $excessGroups), get_debug_type($constraint), static::class));
101+
foreach ($nestedConstraints as $constraint) {
102+
if (isset(((array) $constraint)['groups'])) {
103+
$excessGroups = array_diff($constraint->groups, $this->groups);
104+
105+
if (\count($excessGroups) > 0) {
106+
throw new ConstraintDefinitionException(\sprintf('The group(s) "%s" passed to the constraint "%s" should also be passed to its containing constraint "%s".', implode('", "', $excessGroups), get_debug_type($constraint), static::class));
107+
}
108+
} else {
109+
$constraint->groups = $this->groups;
102110
}
103-
} else {
104-
$constraint->groups = $this->groups;
105111
}
106-
}
107112

108-
$this->$compositeOption = $nestedConstraints;
113+
$this->$compositeOption = $nestedConstraints;
114+
}
109115
}
110116

111117
/**
@@ -115,18 +121,25 @@ public function addImplicitGroupName(string $group): void
115121
{
116122
parent::addImplicitGroupName($group);
117123

118-
/** @var Constraint[] $nestedConstraints */
119-
$nestedConstraints = $this->{$this->getCompositeOption()};
124+
$compositeOptions = $this->getCompositeOption();
125+
if (!\is_array($compositeOptions)) {
126+
$compositeOptions = [$compositeOptions];
127+
}
128+
129+
foreach ($compositeOptions as $compositeOption) {
130+
/* @var Constraint[] $nestedConstraints */
131+
$nestedConstraints = $this->$compositeOption;
120132

121-
foreach ($nestedConstraints as $constraint) {
122-
$constraint->addImplicitGroupName($group);
133+
foreach ($nestedConstraints as $constraint) {
134+
$constraint->addImplicitGroupName($group);
135+
}
123136
}
124137
}
125138

126139
/**
127140
* Returns the name of the property that contains the nested constraints.
128141
*/
129-
abstract protected function getCompositeOption(): string;
142+
abstract protected function getCompositeOption(): array|string;
130143

131144
/**
132145
* @internal Used by metadata
@@ -135,8 +148,17 @@ abstract protected function getCompositeOption(): string;
135148
*/
136149
public function getNestedConstraints(): array
137150
{
138-
/* @var Constraint[] $nestedConstraints */
139-
return $this->{$this->getCompositeOption()};
151+
$compositeOptions = $this->getCompositeOption();
152+
if (!\is_array($compositeOptions)) {
153+
$compositeOptions = [$compositeOptions];
154+
}
155+
156+
$constraints = [];
157+
foreach ($compositeOptions as $compositeOption) {
158+
$constraints = array_merge($constraints, (array) $this->$compositeOption);
159+
}
160+
161+
return $constraints;
140162
}
141163

142164
/**

src/Symfony/Component/Validator/Constraints/When.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,18 @@ class When extends Composite
2828
public string|Expression $expression;
2929
public array|Constraint $constraints = [];
3030
public array $values = [];
31+
public array|Constraint $otherwise = [];
3132

3233
/**
3334
* @param string|Expression|array<string,mixed> $expression The condition to evaluate, written with the ExpressionLanguage syntax
3435
* @param Constraint[]|Constraint|null $constraints One or multiple constraints that are applied if the expression returns true
3536
* @param array<string,mixed>|null $values The values of the custom variables used in the expression (defaults to [])
3637
* @param string[]|null $groups
3738
* @param array<string,mixed>|null $options
39+
* @param Constraint[]|Constraint $otherwise One or multiple constraints that are applied if the expression returns false
3840
*/
3941
#[HasNamedArguments]
40-
public function __construct(string|Expression|array $expression, array|Constraint|null $constraints = null, ?array $values = null, ?array $groups = null, $payload = null, ?array $options = null)
42+
public function __construct(string|Expression|array $expression, array|Constraint|null $constraints = null, ?array $values = null, ?array $groups = null, $payload = null, ?array $options = null, array|Constraint $otherwise = [])
4143
{
4244
if (!class_exists(ExpressionLanguage::class)) {
4345
throw new LogicException(\sprintf('The "symfony/expression-language" component is required to use the "%s" constraint. Try running "composer require symfony/expression-language".', __CLASS__));
@@ -56,12 +58,17 @@ public function __construct(string|Expression|array $expression, array|Constrain
5658

5759
$options['expression'] = $expression;
5860
$options['constraints'] = $constraints;
61+
$options['otherwise'] = $otherwise;
5962
}
6063

6164
if (isset($options['constraints']) && !\is_array($options['constraints'])) {
6265
$options['constraints'] = [$options['constraints']];
6366
}
6467

68+
if (isset($options['otherwise']) && !\is_array($options['otherwise'])) {
69+
$options['otherwise'] = [$options['otherwise']];
70+
}
71+
6572
if (null !== $groups) {
6673
$options['groups'] = $groups;
6774
}
@@ -85,8 +92,8 @@ public function getTargets(): string|array
8592
return [self::CLASS_CONSTRAINT, self::PROPERTY_CONSTRAINT];
8693
}
8794

88-
protected function getCompositeOption(): string
95+
protected function getCompositeOption(): array|string
8996
{
90-
return 'constraints';
97+
return ['constraints', 'otherwise'];
9198
}
9299
}

src/Symfony/Component/Validator/Constraints/WhenValidator.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,14 @@ public function validate(mixed $value, Constraint $constraint): void
3535
$variables['this'] = $context->getObject();
3636
$variables['context'] = $context;
3737

38-
if ($this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) {
38+
$result = $this->getExpressionLanguage()->evaluate($constraint->expression, $variables);
39+
40+
if ($result) {
3941
$context->getValidator()->inContext($context)
4042
->validate($value, $constraint->constraints);
43+
} elseif ($constraint->otherwise) {
44+
$context->getValidator()->inContext($context)
45+
->validate($value, $constraint->otherwise);
4146
}
4247
}
4348

0 commit comments

Comments
 (0)
0