diff --git a/UPGRADE-7.4.md b/UPGRADE-7.4.md
index 63bd8866cef4b..abb49bfe6e20e 100644
--- a/UPGRADE-7.4.md
+++ b/UPGRADE-7.4.md
@@ -58,10 +58,11 @@ Translation
Validator
---------
+ * Deprecate passing a list of choices to the first argument of the `Choice` constraint. Use the `choices` option instead
* Deprecate `getRequiredOptions()` and `getDefaultOption()` methods of the `All`, `AtLeastOneOf`, `CardScheme`, `Collection`,
`CssColor`, `Expression`, `Regex`, `Sequentially`, `Type`, and `When` constraints
* Deprecate evaluating options in the base `Constraint` class. Initialize properties in the constructor of the concrete constraint
- class instead.
+ class instead
*Before*
@@ -97,7 +98,7 @@ Validator
}
```
- * Deprecate the `getRequiredOptions()` method of the base `Constraint` class. Use mandatory constructor arguments instead.
+ * Deprecate the `getRequiredOptions()` method of the base `Constraint` class. Use mandatory constructor arguments instead
*Before*
@@ -137,10 +138,10 @@ Validator
}
}
```
- * Deprecate the `normalizeOptions()` and `getDefaultOption()` methods of the base `Constraint` class without replacements.
- Overriding them in child constraint will not have any effects starting with Symfony 8.0.
+ * Deprecate the `normalizeOptions()` and `getDefaultOption()` methods of the base `Constraint` class without replacements;
+ overriding them in child constraint will not have any effects starting with Symfony 8.0
* Deprecate passing an array of options to the `Composite` constraint class. Initialize the properties referenced with `getNestedConstraints()`
- in child classes before calling the constructor of `Composite`.
+ in child classes before calling the constructor of `Composite`
*Before*
diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md
index f635c68daea5f..c95fc1b84ade7 100644
--- a/src/Symfony/Component/Validator/CHANGELOG.md
+++ b/src/Symfony/Component/Validator/CHANGELOG.md
@@ -4,6 +4,7 @@ CHANGELOG
7.4
---
+ * Deprecate passing a list of choices to the first argument of the `Choice` constraint. Use the `choices` option instead
* Add the `min` and `max` parameter to the `Length` constraint violation
* Deprecate `getRequiredOptions()` and `getDefaultOption()` methods of the `All`, `AtLeastOneOf`, `CardScheme`, `Collection`,
`CssColor`, `Expression`, `Regex`, `Sequentially`, `Type`, and `When` constraints
diff --git a/src/Symfony/Component/Validator/Constraints/Choice.php b/src/Symfony/Component/Validator/Constraints/Choice.php
index 8bc380735d1c4..cf353907d8e2b 100644
--- a/src/Symfony/Component/Validator/Constraints/Choice.php
+++ b/src/Symfony/Component/Validator/Constraints/Choice.php
@@ -85,6 +85,7 @@ public function __construct(
?bool $match = null,
) {
if (\is_array($options) && $options && array_is_list($options)) {
+ trigger_deprecation('symfony/validator', '7.4', 'Support for passing the choices as the first argument to %s is deprecated.', static::class);
$choices ??= $options;
$options = null;
} elseif (\is_array($options) && [] !== $options) {
diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AtLeastOneOfValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/AtLeastOneOfValidatorTest.php
index 22b53dd13cbe1..59b737edc8fbd 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/AtLeastOneOfValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/AtLeastOneOfValidatorTest.php
@@ -317,7 +317,7 @@ public function testValidateNestedAtLeaseOneOfConstraints()
new Collection([
'bar' => new AtLeastOneOf([
new Type('int'),
- new Choice(['test1', 'test2']),
+ new Choice(choices: ['test1', 'test2']),
]),
]),
new Collection([
diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceTest.php
index 2173c45f52055..ddfb31b113e87 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceTest.php
@@ -66,7 +66,7 @@ class ChoiceDummy
#[Choice(choices: ['foo', 'bar'], message: 'myMessage')]
private $b;
- #[Choice([1, 2], groups: ['my_group'], payload: 'some attached data')]
+ #[Choice(choices: [1, 2], groups: ['my_group'], payload: 'some attached data')]
private $c;
#[Choice(choices: ['one' => 1, 'two' => 2])]
diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php
index 39affe4421a03..acfbb84195ce9 100644
--- a/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Constraints/ChoiceValidatorTest.php
@@ -74,20 +74,21 @@ public function testValidCallbackExpected()
$this->validator->validate('foobar', new Choice(callback: 'abcd'));
}
- /**
- * @dataProvider provideConstraintsWithChoicesArray
- */
- public function testValidChoiceArray(Choice $constraint)
+ public function testValidChoiceArray()
{
- $this->validator->validate('bar', $constraint);
+ $this->validator->validate('bar', new Choice(choices: ['foo', 'bar']));
$this->assertNoViolation();
}
- public static function provideConstraintsWithChoicesArray(): iterable
+ /**
+ * @group legacy
+ */
+ public function testValidChoiceArrayFirstArgument()
{
- yield 'first argument' => [new Choice(['foo', 'bar'])];
- yield 'named arguments' => [new Choice(choices: ['foo', 'bar'])];
+ $this->validator->validate('bar', new Choice(['foo', 'bar']));
+
+ $this->assertNoViolation();
}
/**
diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Loader/XmlFileLoaderTest.php
index 08a4bb862cbf1..3b0872df274fe 100644
--- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/XmlFileLoaderTest.php
@@ -22,6 +22,7 @@
use Symfony\Component\Validator\Constraints\Range;
use Symfony\Component\Validator\Constraints\Regex;
use Symfony\Component\Validator\Constraints\Traverse;
+use Symfony\Component\Validator\Constraints\Type;
use Symfony\Component\Validator\Exception\MappingException;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\XmlFileLoader;
@@ -76,8 +77,6 @@ public function testLoadClassMetadata()
$expected->addConstraint(new ConstraintWithNamedArguments(['foo', 'bar']));
$expected->addConstraint(new ConstraintWithoutValueWithNamedArguments(['foo']));
$expected->addPropertyConstraint('firstName', new NotNull());
- $expected->addPropertyConstraint('firstName', new Range(min: 3));
- $expected->addPropertyConstraint('firstName', new Choice(['A', 'B']));
$expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)]));
$expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)]));
$expected->addPropertyConstraint('firstName', new Collection(fields: [
@@ -95,6 +94,23 @@ public function testLoadClassMetadata()
$this->assertEquals($expected, $metadata);
}
+ /**
+ * @group legacy
+ */
+ public function testLoadClassMetadataValueOption()
+ {
+ $loader = new XmlFileLoader(__DIR__.'/constraint-mapping-value-option.xml');
+ $metadata = new ClassMetadata(Entity::class);
+
+ $loader->loadClassMetadata($metadata);
+
+ $expected = new ClassMetadata(Entity::class);
+ $expected->addPropertyConstraint('firstName', new Type(type: 'string'));
+ $expected->addPropertyConstraint('firstName', new Choice(choices: ['A', 'B']));
+
+ $this->assertEquals($expected, $metadata);
+ }
+
public function testLoadClassMetadataWithNonStrings()
{
$loader = new XmlFileLoader(__DIR__.'/constraint-mapping-non-strings.xml');
diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Loader/YamlFileLoaderTest.php
index c3bbcb18e1683..9cf77fc38303a 100644
--- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/YamlFileLoaderTest.php
@@ -20,6 +20,7 @@
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\NotNull;
use Symfony\Component\Validator\Constraints\Range;
+use Symfony\Component\Validator\Constraints\Type;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\YamlFileLoader;
use Symfony\Component\Validator\Tests\Dummy\DummyGroupProvider;
@@ -120,8 +121,6 @@ public function testLoadClassMetadata()
$expected->addConstraint(new ConstraintWithNamedArguments('foo'));
$expected->addConstraint(new ConstraintWithNamedArguments(['foo', 'bar']));
$expected->addPropertyConstraint('firstName', new NotNull());
- $expected->addPropertyConstraint('firstName', new Range(min: 3));
- $expected->addPropertyConstraint('firstName', new Choice(['A', 'B']));
$expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)]));
$expected->addPropertyConstraint('firstName', new All(constraints: [new NotNull(), new Range(min: 3)]));
$expected->addPropertyConstraint('firstName', new Collection(fields: [
@@ -139,6 +138,23 @@ public function testLoadClassMetadata()
$this->assertEquals($expected, $metadata);
}
+ /**
+ * @group legacy
+ */
+ public function testLoadClassMetadataValueOption()
+ {
+ $loader = new YamlFileLoader(__DIR__.'/constraint-mapping-value-option.yml');
+ $metadata = new ClassMetadata(Entity::class);
+
+ $loader->loadClassMetadata($metadata);
+
+ $expected = new ClassMetadata(Entity::class);
+ $expected->addPropertyConstraint('firstName', new Type(type: 'string'));
+ $expected->addPropertyConstraint('firstName', new Choice(choices: ['A', 'B']));
+
+ $this->assertEquals($expected, $metadata);
+ }
+
public function testLoadClassMetadataWithConstants()
{
$loader = new YamlFileLoader(__DIR__.'/mapping-with-constants.yml');
diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping-value-option.xml b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping-value-option.xml
new file mode 100644
index 0000000000000..d0fea931d4415
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping-value-option.xml
@@ -0,0 +1,27 @@
+
+
+
+
+ Symfony\Component\Validator\Tests\Fixtures\
+
+
+
+
+
+
+
+
+ string
+
+
+
+ A
+ B
+
+
+
+
+
+
diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping-value-option.yml b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping-value-option.yml
new file mode 100644
index 0000000000000..149497ad1b7b9
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping-value-option.yml
@@ -0,0 +1,10 @@
+namespaces:
+ custom: Symfony\Component\Validator\Tests\Fixtures\
+
+Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity:
+ properties:
+ firstName:
+ # Constraint with single value
+ - Type: string
+ # Constraint with multiple values
+ - Choice: [A, B]
diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.xml b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.xml
index 8a7975f114137..3666d3a757e4a 100644
--- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.xml
+++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.xml
@@ -59,17 +59,6 @@
-
-
-
-
-
-
-
- A
- B
-
-
diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.yml b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.yml
index af091a89fad8b..06b0bd44f2aef 100644
--- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.yml
+++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.yml
@@ -26,11 +26,6 @@ Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity:
firstName:
# Constraint without value
- NotNull: ~
- # Constraint with single value
- - Range:
- min: 3
- # Constraint with multiple values
- - Choice: [A, B]
# Constraint with child constraints
- All:
- NotNull: ~