diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index 297904b5e7293..c7b8805f55b2a 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * Deprecate constraint `ExpressionLanguageSyntax`, use `ExpressionSyntax` instead * Add method `__toString()` to `ConstraintViolationInterface` & `ConstraintViolationListInterface` * Allow creating constraints with required arguments + * Add the `match` option to the `Choice` constraint 6.0 --- diff --git a/src/Symfony/Component/Validator/Constraints/Choice.php b/src/Symfony/Component/Validator/Constraints/Choice.php index 282366603a8e9..226a08d6e6479 100644 --- a/src/Symfony/Component/Validator/Constraints/Choice.php +++ b/src/Symfony/Component/Validator/Constraints/Choice.php @@ -47,6 +47,7 @@ class Choice extends Constraint public $multipleMessage = 'One or more of the given values is invalid.'; public $minMessage = 'You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.'; public $maxMessage = 'You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.'; + public bool $match = true; /** * {@inheritdoc} @@ -69,7 +70,8 @@ public function __construct( string $minMessage = null, string $maxMessage = null, array $groups = null, - mixed $payload = null + mixed $payload = null, + bool $match = null, ) { if (\is_array($options) && $options && array_is_list($options)) { $choices ??= $options; @@ -90,5 +92,6 @@ public function __construct( $this->multipleMessage = $multipleMessage ?? $this->multipleMessage; $this->minMessage = $minMessage ?? $this->minMessage; $this->maxMessage = $maxMessage ?? $this->maxMessage; + $this->match = $match ?? $this->match; } } diff --git a/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php b/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php index a193436759bdd..0be50f96e9c4b 100644 --- a/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php @@ -65,7 +65,7 @@ public function validate(mixed $value, Constraint $constraint) if ($constraint->multiple) { foreach ($value as $_value) { - if (!\in_array($_value, $choices, true)) { + if ($constraint->match xor \in_array($_value, $choices, true)) { $this->context->buildViolation($constraint->multipleMessage) ->setParameter('{{ value }}', $this->formatValue($_value)) ->setParameter('{{ choices }}', $this->formatValues($choices)) @@ -98,7 +98,7 @@ public function validate(mixed $value, Constraint $constraint) return; } - } elseif (!\in_array($value, $choices, true)) { + } elseif ($constraint->match xor \in_array($value, $choices, true)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setParameter('{{ choices }}', $this->formatValues($choices)) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php index d3e1acece4d14..f2787cb123948 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php @@ -343,4 +343,34 @@ public function testStrictWithMultipleChoices() ->setCode(Choice::NO_SUCH_CHOICE_ERROR) ->assertRaised(); } + + public function testMatchFalse() + { + $this->validator->validate('foo', new Choice([ + 'choices' => ['foo', 'bar'], + 'match' => false, + ])); + + $this->buildViolation('The value you selected is not a valid choice.') + ->setParameter('{{ value }}', '"foo"') + ->setParameter('{{ choices }}', '"foo", "bar"') + ->setCode(Choice::NO_SUCH_CHOICE_ERROR) + ->assertRaised(); + } + + public function testMatchFalseWithMultiple() + { + $this->validator->validate(['ccc', 'bar', 'zzz'], new Choice([ + 'choices' => ['foo', 'bar'], + 'multiple' => true, + 'match' => false, + ])); + + $this->buildViolation('One or more of the given values is invalid.') + ->setParameter('{{ value }}', '"bar"') + ->setParameter('{{ choices }}', '"foo", "bar"') + ->setCode(Choice::NO_SUCH_CHOICE_ERROR) + ->setInvalidValue('bar') + ->assertRaised(); + } }