From e7ba2be6a9c7b9d2684406ef562a1b5b3df16a8a Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Thu, 29 May 2025 10:13:36 -0700
Subject: [PATCH 01/18] Issue #3522497: Pass named arguments to
 HasNamedArguments plugins.

---
 core/.deprecation-ignore.txt                          | 3 ---
 core/lib/Drupal/Core/Validation/ConstraintFactory.php | 8 +++++++-
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/core/.deprecation-ignore.txt b/core/.deprecation-ignore.txt
index 12772fbb55bc..4dcdec3e433b 100644
--- a/core/.deprecation-ignore.txt
+++ b/core/.deprecation-ignore.txt
@@ -39,6 +39,3 @@
 %The "Drupal\\Core\\Database\\Query\\SelectExtender::hasAnyTag\(\)" method will require a new "string \.\.\. \$tags" argument in the next major version of its interface%
 %The "Drupal\\Core\\Entity\\Query\\QueryBase::hasAllTags\(\)" method will require a new "string \.\.\. \$tags" argument in the next major version of its interface%
 %The "Drupal\\Core\\Entity\\Query\\QueryBase::hasAnyTag\(\)" method will require a new "string \.\.\. \$tags" argument in the next major version of its interface%
-
-# Symfony 7.3.
-%Since symfony/validator 7.3: Passing an array of options to configure the "[^"]+" constraint is deprecated, use named arguments instead.%
diff --git a/core/lib/Drupal/Core/Validation/ConstraintFactory.php b/core/lib/Drupal/Core/Validation/ConstraintFactory.php
index 3196dae1cb0b..b6d73357a7a9 100644
--- a/core/lib/Drupal/Core/Validation/ConstraintFactory.php
+++ b/core/lib/Drupal/Core/Validation/ConstraintFactory.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core\Validation;
 
 use Drupal\Core\Plugin\Factory\ContainerFactory;
+use Symfony\Component\Validator\Attribute\HasNamedArguments;
 use Symfony\Component\Validator\Constraint;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 
@@ -28,7 +29,12 @@ public function createInstance($plugin_id, array $configuration = []) {
 
     // If the plugin is a Symfony Constraint, use the correct constructor.
     if (is_subclass_of($plugin_class, Constraint::class)) {
-      return new $plugin_class($configuration);
+      if (empty($configuration)) {
+        return new $plugin_class();
+      }
+
+      $has_named_arguments = (bool) (new \ReflectionMethod($plugin_class, '__construct'))->getAttributes(HasNamedArguments::class);
+      return $has_named_arguments ? new $plugin_class(...$configuration) : new $plugin_class($configuration);
     }
 
     // Otherwise, create the plugin as normal.
-- 
GitLab


From ae0035c27bceb488b97cd1883995ec205bb6e461 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Thu, 29 May 2025 10:47:30 -0700
Subject: [PATCH 02/18] First test fixes.

---
 .../Plugin/Validation/Constraint/CountryCodeConstraint.php     | 3 +--
 .../Plugin/Validation/Constraint/RangeConstraint.php           | 2 ++
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountryCodeConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountryCodeConstraint.php
index 715607e3b668..5f1557468e9d 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountryCodeConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountryCodeConstraint.php
@@ -25,8 +25,7 @@ class CountryCodeConstraint implements ContainerFactoryPluginInterface {
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): Choice {
     $countries = $container->get(CountryManagerInterface::class)->getList();
-    $configuration['choices'] = array_keys($countries);
-    return new Choice($configuration);
+    return new Choice(choices: array_keys($countries));
   }
 
 }
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/RangeConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/RangeConstraint.php
index 8a18df076c58..2f6dacbaf9dc 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/RangeConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/RangeConstraint.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
+use Symfony\Component\Validator\Attribute\HasNamedArguments;
 use Symfony\Component\Validator\Constraints\Range;
 
 /**
@@ -23,6 +24,7 @@ class RangeConstraint extends Range {
   /**
    * {@inheritdoc}
    */
+  #[HasNamedArguments]
   public function __construct(...$args) {
     $this->notInRangeMessage = 'This value should be between %min and %max.';
     $this->minMessage = 'This value should be %limit or more.';
-- 
GitLab


From 07b968c4ba856aedf67970b567409fd4ce2b2b73 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Thu, 29 May 2025 11:58:20 -0700
Subject: [PATCH 03/18] Fix tests 2.

---
 core/config/schema/core.data_types.schema.yml        |  3 ++-
 .../lib/Drupal/Core/Validation/ConstraintFactory.php | 12 ++++++++++--
 core/modules/block/config/schema/block.schema.yml    |  3 ++-
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index 8bbffd2acb29..a52bc7576b62 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -191,7 +191,8 @@ _core_config_info:
       label: 'Default configuration hash'
       constraints:
         NotNull: []
-        Regex: '/^[a-zA-Z0-9\-_]+$/'
+        Regex:
+          pattern: '/^[a-zA-Z0-9\-_]+$/'
         # The hash is a base64-encoded version of the config's SHA-256 hash. Given
         # the deterministic length of a SHA-256 hash, and the way base64 encoding
         # works, this is always going to be 43 characters long.
diff --git a/core/lib/Drupal/Core/Validation/ConstraintFactory.php b/core/lib/Drupal/Core/Validation/ConstraintFactory.php
index b6d73357a7a9..525244093624 100644
--- a/core/lib/Drupal/Core/Validation/ConstraintFactory.php
+++ b/core/lib/Drupal/Core/Validation/ConstraintFactory.php
@@ -29,11 +29,19 @@ public function createInstance($plugin_id, array $configuration = []) {
 
     // If the plugin is a Symfony Constraint, use the correct constructor.
     if (is_subclass_of($plugin_class, Constraint::class)) {
-      if (empty($configuration)) {
+      if (!empty($configuration) && array_is_list($configuration)) {
+        return new $plugin_class($configuration);
+      }
+
+      $reflection_method = new \ReflectionMethod($plugin_class, '__construct');
+      if (!$configuration) {
+        if ($reflection_method->getNumberOfRequiredParameters() > 0) {
+          return new $plugin_class($configuration);
+        }
         return new $plugin_class();
       }
 
-      $has_named_arguments = (bool) (new \ReflectionMethod($plugin_class, '__construct'))->getAttributes(HasNamedArguments::class);
+      $has_named_arguments = (bool) $reflection_method->getAttributes(HasNamedArguments::class);
       return $has_named_arguments ? new $plugin_class(...$configuration) : new $plugin_class($configuration);
     }
 
diff --git a/core/modules/block/config/schema/block.schema.yml b/core/modules/block/config/schema/block.schema.yml
index 9c28c0b37232..f9c8f9927391 100644
--- a/core/modules/block/config/schema/block.schema.yml
+++ b/core/modules/block/config/schema/block.schema.yml
@@ -28,7 +28,8 @@ block.block.*:
       label: 'Region'
       constraints:
         NotBlank: []
-        Callback: ['\Drupal\block\Entity\Block', validateRegion]
+        Callback:
+          callback: ['\Drupal\block\Entity\Block', validateRegion]
     weight:
       type: weight
       label: 'Weight'
-- 
GitLab


From ec45dda90ccd69b919ca4ec02b9aa33047aa09ce Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Thu, 29 May 2025 12:24:18 -0700
Subject: [PATCH 04/18] Fix tests 3.

---
 .../src/SecurityAdvisories/SecurityAdvisory.php    | 14 +++++++-------
 core/modules/update/src/ProjectRelease.php         |  8 ++++----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/core/modules/system/src/SecurityAdvisories/SecurityAdvisory.php b/core/modules/system/src/SecurityAdvisories/SecurityAdvisory.php
index 4ff3768c1ce4..b6d8a094fe7b 100644
--- a/core/modules/system/src/SecurityAdvisories/SecurityAdvisory.php
+++ b/core/modules/system/src/SecurityAdvisories/SecurityAdvisory.php
@@ -120,22 +120,22 @@ public static function createFromArray(array $data): self {
    */
   protected static function validateAdvisoryData(array $data): void {
     $not_blank_constraints = [
-      new Type(['type' => 'string']),
+      new Type(type: 'string'),
       new NotBlank(),
     ];
-    $collection_constraint = new Collection([
-      'fields' => [
+    $collection_constraint = new Collection(
+      fields: [
         'title' => $not_blank_constraints,
         'project' => $not_blank_constraints,
         'type' => $not_blank_constraints,
         'link' => $not_blank_constraints,
-        'is_psa' => new Choice(['choices' => [1, '1', 0, '0', TRUE, FALSE]]),
-        'insecure' => new Type(['type' => 'array']),
+        'is_psa' => new Choice(choices: [1, '1', 0, '0', TRUE, FALSE]),
+        'insecure' => new Type(type: 'array'),
       ],
       // Allow unknown fields, in the case that new fields are added to JSON
       // feed validation should still pass.
-      'allowExtraFields' => TRUE,
-    ]);
+      allowExtraFields:TRUE,
+    );
     $violations = Validation::createValidator()->validate($data, $collection_constraint);
     if ($violations->count()) {
       foreach ($violations as $violation) {
diff --git a/core/modules/update/src/ProjectRelease.php b/core/modules/update/src/ProjectRelease.php
index 899eb9b836f7..8c3532fbc4f1 100644
--- a/core/modules/update/src/ProjectRelease.php
+++ b/core/modules/update/src/ProjectRelease.php
@@ -143,8 +143,8 @@ private static function validateReleaseData(array $data): void {
       new Type('string'),
       new NotBlank(),
     ];
-    $collection_constraint = new Collection([
-      'fields' => [
+    $collection_constraint = new Collection(
+      fields: [
         'version' => $not_blank_constraints,
         'date' => new Optional([new Type('numeric')]),
         'core_compatible' => new Optional([new Type('boolean')]),
@@ -161,8 +161,8 @@ private static function validateReleaseData(array $data): void {
           ]),
         ]),
       ],
-      'allowExtraFields' => TRUE,
-    ]);
+      allowExtraFields:TRUE,
+    );
     $violations = Validation::createValidator()->validate($data, $collection_constraint);
     if (count($violations)) {
       foreach ($violations as $violation) {
-- 
GitLab


From bc0075a347044de2902697342a60259b8515325a Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Thu, 29 May 2025 13:00:55 -0700
Subject: [PATCH 05/18] Fix tests 4.

---
 core/config/schema/core.data_types.schema.yml        |  3 ++-
 .../Constraint/MenuLinkDepthConstraint.php           | 12 +++++++++---
 .../Constraint/AllowedValuesConstraint.php           |  2 ++
 .../Plugin/Validation/Constraint/EmailConstraint.php |  2 ++
 .../Validation/Constraint/LengthConstraint.php       |  2 ++
 .../Constraint/UserCancelMethodsConstraint.php       |  3 +--
 6 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index a52bc7576b62..cb67da15333d 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -196,7 +196,8 @@ _core_config_info:
         # The hash is a base64-encoded version of the config's SHA-256 hash. Given
         # the deterministic length of a SHA-256 hash, and the way base64 encoding
         # works, this is always going to be 43 characters long.
-        Length: 43
+        Length:
+          exactly: 43
   constraints:
     ValidKeys: ['default_config_hash']
 
diff --git a/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php b/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php
index b038960ed808..1df6bfc99ec7 100644
--- a/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php
+++ b/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php
@@ -7,6 +7,7 @@
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
 use Drupal\Core\Validation\Plugin\Validation\Constraint\RangeConstraint;
+use Symfony\Component\Validator\Attribute\HasNamedArguments;
 
 /**
  * Validates the link depth of a menu tree.
@@ -19,10 +20,15 @@
 class MenuLinkDepthConstraint extends RangeConstraint {
 
   /**
-   * The initial level of menu items that are being exposed (zero-based).
+   * @param string|int $baseLevel
+   *   The initial level of menu items that are being exposed (zero-based).
+   * @param array<string, mixed> $args
+   *   Additional arguments to pass to parent constructor.
    *
-   * @var string|int
    */
-  public string|int $baseLevel = 0;
+  #[HasNamedArguments]
+  public function __construct(public readonly string|int $baseLevel = 0, ...$args) {
+    parent::__construct(...$args);
+  }
 
 }
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php
index 18acb7abd7bc..871b4fb8a68f 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
+use Symfony\Component\Validator\Attribute\HasNamedArguments;
 use Symfony\Component\Validator\Constraints\Choice;
 
 /**
@@ -20,6 +21,7 @@ class AllowedValuesConstraint extends Choice {
   /**
    * {@inheritdoc}
    */
+  #[HasNamedArguments]
   public function __construct(...$args) {
     $this->strict = TRUE;
     $this->minMessage = 'You must select at least %limit choice.|You must select at least %limit choices.';
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EmailConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EmailConstraint.php
index fe0adf3720ed..a3bdef9ee8e1 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EmailConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EmailConstraint.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
+use Symfony\Component\Validator\Attribute\HasNamedArguments;
 use Symfony\Component\Validator\Constraints\Email;
 use Symfony\Component\Validator\Constraints\EmailValidator;
 
@@ -21,6 +22,7 @@ class EmailConstraint extends Email {
   /**
    * {@inheritdoc}
    */
+  #[HasNamedArguments]
   public function __construct(...$args) {
     $this->mode = static::VALIDATION_MODE_STRICT;
     parent::__construct(...$args);
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/LengthConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/LengthConstraint.php
index 021603c7e826..078db9474c25 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/LengthConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/LengthConstraint.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
+use Symfony\Component\Validator\Attribute\HasNamedArguments;
 use Symfony\Component\Validator\Constraints\Length;
 
 /**
@@ -23,6 +24,7 @@ class LengthConstraint extends Length {
   /**
    * {@inheritdoc}
    */
+  #[HasNamedArguments]
   public function __construct(...$args) {
     $this->maxMessage = 'This value is too long. It should have %limit character or less.|This value is too long. It should have %limit characters or less.';
     $this->minMessage = 'This value is too short. It should have %limit character or more.|This value is too short. It should have %limit characters or more.';
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserCancelMethodsConstraint.php b/core/modules/user/src/Plugin/Validation/Constraint/UserCancelMethodsConstraint.php
index 44034b3d3db2..fff7aceb0d7b 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserCancelMethodsConstraint.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserCancelMethodsConstraint.php
@@ -23,8 +23,7 @@ class UserCancelMethodsConstraint implements ContainerFactoryPluginInterface {
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): Choice {
-    $configuration['choices'] = array_keys(user_cancel_methods()['#options']);
-    return new Choice($configuration);
+    return new Choice(choices: array_keys(user_cancel_methods()['#options']));
   }
 
 }
-- 
GitLab


From 97a7b38375fd196ff3c118dd6fe18b7e7ea0ed3c Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Thu, 29 May 2025 13:03:46 -0700
Subject: [PATCH 06/18] PHPCS.

---
 .../Plugin/Validation/Constraint/MenuLinkDepthConstraint.php     | 1 -
 1 file changed, 1 deletion(-)

diff --git a/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php b/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php
index 1df6bfc99ec7..1ef9deac339d 100644
--- a/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php
+++ b/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php
@@ -24,7 +24,6 @@ class MenuLinkDepthConstraint extends RangeConstraint {
    *   The initial level of menu items that are being exposed (zero-based).
    * @param array<string, mixed> $args
    *   Additional arguments to pass to parent constructor.
-   *
    */
   #[HasNamedArguments]
   public function __construct(public readonly string|int $baseLevel = 0, ...$args) {
-- 
GitLab


From 051845c2d2a4f82aa93052ceae40584382a90640 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Thu, 29 May 2025 13:28:24 -0700
Subject: [PATCH 07/18] Fix tests 5.

---
 .../Plugin/Validation/Constraint/CountConstraint.php          | 2 ++
 .../Core/TypedData/AllowedValuesConstraintValidatorTest.php   | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountConstraint.php
index 9cde560037e5..7c95d25d8f46 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountConstraint.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
+use Symfony\Component\Validator\Attribute\HasNamedArguments;
 use Symfony\Component\Validator\Constraints\Count;
 
 /**
@@ -21,6 +22,7 @@ class CountConstraint extends Count {
   /**
    * {@inheritdoc}
    */
+  #[HasNamedArguments]
   public function __construct(...$args) {
     $this->minMessage = 'This collection should contain %limit element or more.|This collection should contain %limit elements or more.';
     $this->maxMessage = 'This collection should contain %limit element or less.|This collection should contain %limit elements or less.';
diff --git a/core/tests/Drupal/KernelTests/Core/TypedData/AllowedValuesConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/TypedData/AllowedValuesConstraintValidatorTest.php
index acefd78cd723..21b5a24a0545 100644
--- a/core/tests/Drupal/KernelTests/Core/TypedData/AllowedValuesConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/TypedData/AllowedValuesConstraintValidatorTest.php
@@ -113,8 +113,8 @@ public function testValidationCallbackException(): void {
       ->addConstraint('AllowedValues', ['choices' => [1, 2, 3], 'callback' => [static::class, 'doesNotExist']]);
     $typed_data = $this->typedData->create($definition, 1);
 
-    $this->expectException(ConstraintDefinitionException::class);
-    $this->expectExceptionMessage('The AllowedValuesConstraint constraint expects a valid callback');
+    $this->expectException(\TypeError::class);
+    $this->expectExceptionMessage('Symfony\Component\Validator\Constraints\Choice::__construct(): Argument #3 ($callback) must be of type callable|string|null, array given');
     $typed_data->validate();
   }
 
-- 
GitLab


From 8fc5de2ad9dbc96b1189f7966864eae82a5f343a Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Thu, 29 May 2025 13:36:02 -0700
Subject: [PATCH 08/18] PHPCS.

---
 .../Core/TypedData/AllowedValuesConstraintValidatorTest.php      | 1 -
 1 file changed, 1 deletion(-)

diff --git a/core/tests/Drupal/KernelTests/Core/TypedData/AllowedValuesConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/TypedData/AllowedValuesConstraintValidatorTest.php
index 21b5a24a0545..625fe2a48d4a 100644
--- a/core/tests/Drupal/KernelTests/Core/TypedData/AllowedValuesConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/TypedData/AllowedValuesConstraintValidatorTest.php
@@ -6,7 +6,6 @@
 
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\KernelTests\KernelTestBase;
-use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
 
 /**
  * Tests AllowedValues validation constraint with both valid and invalid values.
-- 
GitLab


From c35d1e34b9c5373248ce9c5ba5fc93677cbce902 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Thu, 29 May 2025 14:26:14 -0700
Subject: [PATCH 09/18] Do not clobber other configuration properties.

---
 .../Plugin/Validation/Constraint/CountryCodeConstraint.php     | 3 ++-
 .../Validation/Constraint/UserCancelMethodsConstraint.php      | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountryCodeConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountryCodeConstraint.php
index 5f1557468e9d..fb81ecabfda2 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountryCodeConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CountryCodeConstraint.php
@@ -25,7 +25,8 @@ class CountryCodeConstraint implements ContainerFactoryPluginInterface {
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): Choice {
     $countries = $container->get(CountryManagerInterface::class)->getList();
-    return new Choice(choices: array_keys($countries));
+    $configuration['choices'] = array_keys($countries);
+    return new Choice(...$configuration);
   }
 
 }
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserCancelMethodsConstraint.php b/core/modules/user/src/Plugin/Validation/Constraint/UserCancelMethodsConstraint.php
index fff7aceb0d7b..f21af4ac4c45 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserCancelMethodsConstraint.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserCancelMethodsConstraint.php
@@ -23,7 +23,8 @@ class UserCancelMethodsConstraint implements ContainerFactoryPluginInterface {
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): Choice {
-    return new Choice(choices: array_keys(user_cancel_methods()['#options']));
+    $configuration['choices'] = array_keys(user_cancel_methods()['#options']);
+    return new Choice(...$configuration);
   }
 
 }
-- 
GitLab


From bcfa2b6d6aec19d70811a358f9229de45e17df49 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Sat, 31 May 2025 12:51:43 -0700
Subject: [PATCH 10/18] Add deprecation to Drupal constraints POC.

---
 .../Context/ContextDefinitionInterface.php    |  6 +++-
 .../Constraint/ConfigExistsConstraint.php     |  4 +--
 ...RequiredIfTranslatableValuesConstraint.php |  4 +--
 .../RequiredConfigDependenciesConstraint.php  |  4 +--
 core/lib/Drupal/Core/Entity/EntityType.php    |  2 +-
 .../Core/Entity/EntityTypeInterface.php       |  6 +++-
 .../Constraint/BundleConstraint.php           |  4 +--
 .../Constraint/EntityChangedConstraint.php    |  4 +--
 .../Constraint/EntityHasFieldConstraint.php   |  4 +--
 .../Constraint/EntityTypeConstraint.php       |  4 +--
 .../EntityUntranslatableFieldsConstraint.php  |  4 +--
 .../ImmutablePropertiesConstraint.php         |  4 +--
 .../Constraint/ReferenceAccessConstraint.php  |  4 +--
 .../Constraint/ValidReferenceConstraint.php   |  4 +--
 .../Constraint/ExtensionExistsConstraint.php  |  4 +--
 .../lib/Drupal/Core/Field/FieldConfigBase.php |  2 +-
 .../Core/Field/FieldConfigInterface.php       |  6 +++-
 .../Constraint/UniquePathAliasConstraint.php  |  4 +--
 .../Constraint/ValidPathConstraint.php        |  4 +--
 .../Core/Plugin/Context/ContextDefinition.php |  2 +-
 .../Constraint/PluginExistsConstraint.php     |  4 +--
 .../Drupal/Core/TypedData/DataDefinition.php  |  2 +-
 .../TypedData/DataDefinitionInterface.php     |  6 +++-
 .../Core/Validation/ConstraintFactory.php     |  6 +++-
 .../Core/Validation/ConstraintManager.php     |  6 ++--
 .../Constraint/AtLeastOneOfConstraint.php     |  3 +-
 .../Constraint/ClassResolverConstraint.php    |  3 +-
 .../Constraint/ComplexDataConstraint.php      |  3 +-
 .../Validation/Constraint/ConstraintBase.php  | 29 +++++++++++++++++++
 .../EntityBundleExistsConstraint.php          |  3 +-
 .../Constraint/FullyValidatableConstraint.php |  3 +-
 .../Constraint/PrimitiveTypeConstraint.php    |  3 +-
 .../Constraint/UniqueFieldConstraint.php      |  3 +-
 .../Constraint/UriHostConstraint.php          |  3 +-
 .../Constraint/ValidKeysConstraint.php        |  3 +-
 .../Constraint/CKEditor5ElementConstraint.php |  4 +--
 ...MediaAndFilterSettingsInSyncConstraint.php |  4 +--
 .../EnabledConfigurablePluginsConstraint.php  |  4 +--
 .../FundamentalCompatibilityConstraint.php    |  4 +--
 .../SourceEditingPreventSelfXssConstraint.php |  4 +--
 .../SourceEditingRedundantTagsConstraint.php  |  4 +--
 .../StyleSensibleElementConstraint.php        |  4 +--
 .../ToolbarItemConditionsMetConstraint.php    |  4 +--
 .../Constraint/ToolbarItemConstraint.php      |  4 +--
 .../ToolbarItemDependencyConstraint.php       |  4 +--
 .../UniqueLabelInListConstraint.php           |  4 +--
 .../Constraint/ModerationStateConstraint.php  |  4 +--
 ...ranslationSynchronizedFieldsConstraint.php |  4 +--
 .../Constraint/DateTimeFormatConstraint.php   |  4 +--
 .../Constraint/FileEncodingConstraint.php     |  4 +--
 .../Constraint/FileExtensionConstraint.php    |  4 +--
 .../FileExtensionSecureConstraint.php         |  4 +--
 .../FileImageDimensionsConstraint.php         |  4 +--
 .../Constraint/FileIsImageConstraint.php      |  4 +--
 .../Constraint/FileNameLengthConstraint.php   |  4 +--
 .../Constraint/FileSizeLimitConstraint.php    |  4 +--
 .../Constraint/FileValidationConstraint.php   |  4 +--
 .../AltTextContainsLlamasConstraint.php       |  4 +--
 .../Constraint/LinkAccessConstraint.php       |  4 +--
 .../LinkExternalProtocolsConstraint.php       |  4 +--
 .../LinkNotExistingInternalConstraint.php     |  4 +--
 .../Constraint/LinkTypeConstraint.php         |  4 +--
 .../Constraint/MediaMappingsConstraint.php    |  4 +--
 .../Constraint/OEmbedResourceConstraint.php   |  4 +--
 .../Constraint/MediaTestConstraint.php        |  4 +--
 .../Constraint/MenuSettingsConstraint.php     |  4 +--
 .../Constraint/PathAliasConstraint.php        |  4 +--
 .../Constraint/RestTestConstraint.php         |  4 +--
 .../Constraint/EntityTestEntityLevel.php      |  4 +--
 .../Constraint/FieldWidgetConstraint.php      |  4 +--
 .../TestValidatedReferenceConstraint.php      |  4 +--
 .../ProtectedUserFieldConstraint.php          |  4 +--
 .../Constraint/RoleExistsConstraint.php       |  4 +--
 .../Constraint/UserMailRequired.php           |  4 +--
 .../Constraint/UserNameConstraint.php         |  4 +--
 .../Constraint/DeletedWorkspaceConstraint.php |  4 +--
 ...eferenceSupportedNewEntitiesConstraint.php |  4 +--
 .../EntityWorkspaceConflictConstraint.php     |  4 +--
 78 files changed, 187 insertions(+), 145 deletions(-)
 create mode 100644 core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ConstraintBase.php

diff --git a/core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php b/core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php
index c18a704081ea..a2682aa00311 100644
--- a/core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php
+++ b/core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php
@@ -154,12 +154,16 @@ public function setConstraints(array $constraints);
    *
    * @param string $constraint_name
    *   The name of the constraint to add, i.e. its plugin id.
+   * phpcs:disable Drupal.Commenting
+   * @todo Uncomment new method parameter signature before drupal:12.0.0.
+   * @see https://www.drupal.org/project/drupal/issues/XXXXXX
    * @param array|null $options
    *   The constraint options as required by the constraint plugin, or NULL.
+   * phpcs:enable
    *
    * @return $this
    */
-  public function addConstraint($constraint_name, $options = NULL);
+  public function addConstraint($constraint_name, /* ?array */$options = NULL);
 
   /**
    * Gets a validation constraint.
diff --git a/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/ConfigExistsConstraint.php b/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/ConfigExistsConstraint.php
index 85d22f5511d2..9db3a41f9abc 100644
--- a/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/ConfigExistsConstraint.php
+++ b/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/ConfigExistsConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks that the value is the name of an existing config object.
@@ -15,7 +15,7 @@
   id: 'ConfigExists',
   label: new TranslatableMarkup('Config exists', [], ['context' => 'Validation'])
 )]
-class ConfigExistsConstraint extends SymfonyConstraint {
+class ConfigExistsConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/LangcodeRequiredIfTranslatableValuesConstraint.php b/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/LangcodeRequiredIfTranslatableValuesConstraint.php
index 2ce27a56b6e4..7552d17c6bb4 100644
--- a/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/LangcodeRequiredIfTranslatableValuesConstraint.php
+++ b/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/LangcodeRequiredIfTranslatableValuesConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for translatable configuration.
@@ -16,7 +16,7 @@
   label: new TranslatableMarkup('Translatable config has langcode', [], ['context' => 'Validation']),
   type: ['config_object']
 )]
-class LangcodeRequiredIfTranslatableValuesConstraint extends SymfonyConstraint {
+class LangcodeRequiredIfTranslatableValuesConstraint extends ConstraintBase {
 
   /**
    * The error message if this config object is missing a `langcode`.
diff --git a/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/RequiredConfigDependenciesConstraint.php b/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/RequiredConfigDependenciesConstraint.php
index c766cee33c64..acd5050f792d 100644
--- a/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/RequiredConfigDependenciesConstraint.php
+++ b/core/lib/Drupal/Core/Config/Plugin/Validation/Constraint/RequiredConfigDependenciesConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks that config dependencies contain specific types of entities.
@@ -15,7 +15,7 @@
   id: 'RequiredConfigDependencies',
   label: new TranslatableMarkup('Required config dependency types', [], ['context' => 'Validation'])
 )]
-class RequiredConfigDependenciesConstraint extends SymfonyConstraint {
+class RequiredConfigDependenciesConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php
index 9eab50b9a222..713b78f7f6b1 100644
--- a/core/lib/Drupal/Core/Entity/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -907,7 +907,7 @@ public function setConstraints(array $constraints) {
   /**
    * {@inheritdoc}
    */
-  public function addConstraint($constraint_name, $options = NULL) {
+  public function addConstraint($constraint_name, /* ?array */$options = NULL) {
     $this->constraints[$constraint_name] = $options;
     return $this;
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
index fb11b64c2dd8..a49ea6a82efa 100644
--- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -803,12 +803,16 @@ public function setConstraints(array $constraints);
    *
    * @param string $constraint_name
    *   The name of the constraint to add, i.e. its plugin id.
+   * phpcs:disable Drupal.Commenting
+   * @todo Uncomment new method parameter signature before drupal:12.0.0.
+   * @see https://www.drupal.org/project/drupal/issues/XXXXXX
    * @param array|null $options
    *   The constraint options as required by the constraint plugin, or NULL.
+   * phpcs:enable
    *
    * @return $this
    */
-  public function addConstraint($constraint_name, $options = NULL);
+  public function addConstraint($constraint_name, /* ?array */$options = NULL);
 
   /**
    * Gets the config dependency info for this entity, if any exists.
diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/BundleConstraint.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/BundleConstraint.php
index ee1c82e30bfc..2e58500901ab 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/BundleConstraint.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/BundleConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks if a value is a valid entity type.
@@ -17,7 +17,7 @@
   label: new TranslatableMarkup('Bundle', [], ['context' => 'Validation']),
   type: ['entity', 'entity_reference']
 )]
-class BundleConstraint extends SymfonyConstraint {
+class BundleConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraint.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraint.php
index 8ec9d2fba6f0..7347863494fb 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraint.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for the entity changed timestamp.
@@ -14,7 +14,7 @@
   label: new TranslatableMarkup('Entity changed', [], ['context' => 'Validation']),
   type: ['entity']
 )]
-class EntityChangedConstraint extends SymfonyConstraint {
+class EntityChangedConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityHasFieldConstraint.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityHasFieldConstraint.php
index 9d872ffdcb1f..367b041413c0 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityHasFieldConstraint.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityHasFieldConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks if a value is an entity that has a specific field.
@@ -14,7 +14,7 @@
   label: new TranslatableMarkup('Entity has field', [], ['context' => 'Validation']),
   type: ['entity']
 )]
-class EntityHasFieldConstraint extends SymfonyConstraint {
+class EntityHasFieldConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityTypeConstraint.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityTypeConstraint.php
index 8cb4009a146e..7435caba6081 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityTypeConstraint.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityTypeConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks if a value is a valid entity type.
@@ -14,7 +14,7 @@
   label: new TranslatableMarkup('Entity type', [], ['context' => 'Validation']),
   type: ['entity', 'entity_reference']
 )]
-class EntityTypeConstraint extends SymfonyConstraint {
+class EntityTypeConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityUntranslatableFieldsConstraint.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityUntranslatableFieldsConstraint.php
index f49012133c8b..0d4ee259590f 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityUntranslatableFieldsConstraint.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityUntranslatableFieldsConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for the entity changed timestamp.
@@ -14,7 +14,7 @@
   label: new TranslatableMarkup('Entity untranslatable fields', [], ['context' => 'Validation']),
   type: ['entity']
 )]
-class EntityUntranslatableFieldsConstraint extends SymfonyConstraint {
+class EntityUntranslatableFieldsConstraint extends ConstraintBase {
 
   /**
    * The message when updating a field but not the current revision.
diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ImmutablePropertiesConstraint.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ImmutablePropertiesConstraint.php
index 8de5b974abc8..3311a2510e2c 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ImmutablePropertiesConstraint.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ImmutablePropertiesConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks if config entity properties have been changed.
@@ -16,7 +16,7 @@
   label: new TranslatableMarkup('Properties are unchanged', [], ['context' => 'Validation']),
   type: ['entity']
 )]
-class ImmutablePropertiesConstraint extends SymfonyConstraint {
+class ImmutablePropertiesConstraint extends ConstraintBase {
 
   /**
    * The error message if an immutable property has been changed.
diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ReferenceAccessConstraint.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ReferenceAccessConstraint.php
index 90b924d90a37..a10d0d316513 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ReferenceAccessConstraint.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ReferenceAccessConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Entity Reference valid reference constraint.
@@ -15,7 +15,7 @@
   id: 'ReferenceAccess',
   label: new TranslatableMarkup('Entity Reference reference access', [], ['context' => 'Validation'])
 )]
-class ReferenceAccessConstraint extends SymfonyConstraint {
+class ReferenceAccessConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraint.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraint.php
index e5bc3de1a73f..6dbb577f642e 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraint.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/ValidReferenceConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Entity Reference valid reference constraint.
@@ -15,7 +15,7 @@
   id: 'ValidReference',
   label: new TranslatableMarkup('Entity Reference valid reference', [], ['context' => 'Validation'])
 )]
-class ValidReferenceConstraint extends SymfonyConstraint {
+class ValidReferenceConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/lib/Drupal/Core/Extension/Plugin/Validation/Constraint/ExtensionExistsConstraint.php b/core/lib/Drupal/Core/Extension/Plugin/Validation/Constraint/ExtensionExistsConstraint.php
index 5a555695311a..b2adcb6f3887 100644
--- a/core/lib/Drupal/Core/Extension/Plugin/Validation/Constraint/ExtensionExistsConstraint.php
+++ b/core/lib/Drupal/Core/Extension/Plugin/Validation/Constraint/ExtensionExistsConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks that the value is the name of an installed extension.
@@ -15,7 +15,7 @@
   id: 'ExtensionExists',
   label: new TranslatableMarkup('Extension exists', [], ['context' => 'Validation'])
 )]
-class ExtensionExistsConstraint extends SymfonyConstraint {
+class ExtensionExistsConstraint extends ConstraintBase {
 
   /**
    * The error message for a non-existent module.
diff --git a/core/lib/Drupal/Core/Field/FieldConfigBase.php b/core/lib/Drupal/Core/Field/FieldConfigBase.php
index 39405c37d437..bab688eac427 100644
--- a/core/lib/Drupal/Core/Field/FieldConfigBase.php
+++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
@@ -590,7 +590,7 @@ public function setConstraints(array $constraints) {
   /**
    * {@inheritdoc}
    */
-  public function addConstraint($constraint_name, $options = NULL) {
+  public function addConstraint($constraint_name, /* ?array */$options = NULL) {
     $this->constraints[$constraint_name] = $options;
     return $this;
   }
diff --git a/core/lib/Drupal/Core/Field/FieldConfigInterface.php b/core/lib/Drupal/Core/Field/FieldConfigInterface.php
index 7075c106dd4a..a1fbb787c899 100644
--- a/core/lib/Drupal/Core/Field/FieldConfigInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldConfigInterface.php
@@ -242,8 +242,12 @@ public function addPropertyConstraints($name, array $constraints);
    *
    * @param string $constraint_name
    *   The name of the constraint to add, i.e. its plugin id.
+   * phpcs:disable Drupal.Commenting
+   * @todo Uncomment new method parameter signature before drupal:12.0.0.
+   * @see https://www.drupal.org/project/drupal/issues/XXXXXX
    * @param array|null $options
    *   The constraint options as required by the constraint plugin, or NULL.
+   * phpcs:enable
    *
    * @return static
    *   The object itself for chaining.
@@ -252,7 +256,7 @@ public function addPropertyConstraints($name, array $constraints);
    * @see \Drupal\Core\Field\FieldConfigInterface::addPropertyConstraints()
    * @see hook_entity_bundle_field_info_alter()
    */
-  public function addConstraint($constraint_name, $options = NULL);
+  public function addConstraint($constraint_name, /* ?array */$options = NULL);
 
   /**
    * Sets the array of validation constraints for the FieldItemList.
diff --git a/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/UniquePathAliasConstraint.php b/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/UniquePathAliasConstraint.php
index b97e12eeb81a..0d7d6fc5e122 100644
--- a/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/UniquePathAliasConstraint.php
+++ b/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/UniquePathAliasConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for unique path alias values.
@@ -13,7 +13,7 @@
   id: 'UniquePathAlias',
   label: new TranslatableMarkup('Unique path alias.', [], ['context' => 'Validation'])
 )]
-class UniquePathAliasConstraint extends SymfonyConstraint {
+class UniquePathAliasConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/ValidPathConstraint.php b/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/ValidPathConstraint.php
index be69501609d1..f6854f65eacc 100644
--- a/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/ValidPathConstraint.php
+++ b/core/lib/Drupal/Core/Path/Plugin/Validation/Constraint/ValidPathConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for valid system paths.
@@ -13,7 +13,7 @@
   id: 'ValidPath',
   label: new TranslatableMarkup('Valid path.', [], ['context' => 'Validation'])
 )]
-class ValidPathConstraint extends SymfonyConstraint {
+class ValidPathConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
index 30859de203a5..99fd9c0f7ec7 100644
--- a/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
@@ -236,7 +236,7 @@ public function setConstraints(array $constraints) {
   /**
    * {@inheritdoc}
    */
-  public function addConstraint($constraint_name, $options = NULL) {
+  public function addConstraint($constraint_name, /* ?array */$options = NULL) {
     $this->constraints[$constraint_name] = $options;
     return $this;
   }
diff --git a/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php b/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
index 83d38ee2bbae..6b7599d6d01c 100644
--- a/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
+++ b/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
@@ -9,7 +9,7 @@
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 use Symfony\Component\Validator\Exception\MissingOptionsException;
 
 /**
@@ -19,7 +19,7 @@
   id: 'PluginExists',
   label: new TranslatableMarkup('Plugin exists', [], ['context' => 'Validation'])
 )]
-class PluginExistsConstraint extends SymfonyConstraint implements ContainerFactoryPluginInterface {
+class PluginExistsConstraint extends ConstraintBase implements ContainerFactoryPluginInterface {
 
   /**
    * The error message if a plugin does not exist.
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinition.php b/core/lib/Drupal/Core/TypedData/DataDefinition.php
index 66145088839e..d4c7e67200ae 100644
--- a/core/lib/Drupal/Core/TypedData/DataDefinition.php
+++ b/core/lib/Drupal/Core/TypedData/DataDefinition.php
@@ -303,7 +303,7 @@ public function setConstraints(array $constraints) {
   /**
    * {@inheritdoc}
    */
-  public function addConstraint($constraint_name, $options = NULL) {
+  public function addConstraint($constraint_name, /* ?array */$options = NULL) {
     $this->definition['constraints'][$constraint_name] = $options;
     return $this;
   }
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
index 2ecada0e5d93..930f3ce68e3d 100644
--- a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
+++ b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
@@ -213,13 +213,17 @@ public function getConstraint($constraint_name);
    *
    * @param string $constraint_name
    *   The name of the constraint to add, i.e. its plugin id.
+   * phpcs:disable Drupal.Commenting
+   * @todo Uncomment new method parameter signature before drupal:12.0.0.
+   * @see https://www.drupal.org/project/drupal/issues/XXXXXX
    * @param array|null $options
    *   The constraint options as required by the constraint plugin, or NULL.
+   * phpcs:enable
    *
    * @return static
    *   The object itself for chaining.
    */
-  public function addConstraint($constraint_name, $options = NULL);
+  public function addConstraint($constraint_name, /* ?array */$options = NULL);
 
   /**
    * Determines whether the data value is internal.
diff --git a/core/lib/Drupal/Core/Validation/ConstraintFactory.php b/core/lib/Drupal/Core/Validation/ConstraintFactory.php
index 525244093624..14874ecb09cc 100644
--- a/core/lib/Drupal/Core/Validation/ConstraintFactory.php
+++ b/core/lib/Drupal/Core/Validation/ConstraintFactory.php
@@ -19,6 +19,9 @@ class ConstraintFactory extends ContainerFactory {
    * {@inheritdoc}
    */
   public function createInstance($plugin_id, array $configuration = []) {
+    $options_not_passed_as_array = !empty($configuration['_options_not_passed_as_array']);
+    unset($configuration['_options_not_passed_as_array']);
+
     $plugin_definition = $this->discovery->getDefinition($plugin_id);
     $plugin_class = static::getPluginClass($plugin_id, $plugin_definition, $this->interface);
 
@@ -29,7 +32,8 @@ public function createInstance($plugin_id, array $configuration = []) {
 
     // If the plugin is a Symfony Constraint, use the correct constructor.
     if (is_subclass_of($plugin_class, Constraint::class)) {
-      if (!empty($configuration) && array_is_list($configuration)) {
+      $configuration_is_list = !empty($configuration) && array_is_list($configuration);
+      if ($options_not_passed_as_array || $configuration_is_list) {
         return new $plugin_class($configuration);
       }
 
diff --git a/core/lib/Drupal/Core/Validation/ConstraintManager.php b/core/lib/Drupal/Core/Validation/ConstraintManager.php
index 00f3c862af45..f94e7788e716 100644
--- a/core/lib/Drupal/Core/Validation/ConstraintManager.php
+++ b/core/lib/Drupal/Core/Validation/ConstraintManager.php
@@ -70,7 +70,7 @@ protected function getDiscovery() {
    *
    * @param string $name
    *   The name or plugin id of the constraint.
-   * @param mixed $options
+   * @param array<string, mixed>|null $options
    *   The options to pass to the constraint class. Required and supported
    *   options depend on the constraint class.
    *
@@ -82,7 +82,9 @@ public function create($name, $options) {
       // Plugins need an array as configuration, so make sure we have one.
       // The constraint classes support passing the options as part of the
       // 'value' key also.
-      $options = isset($options) ? ['value' => $options] : [];
+      // @phpstan-ignore isset.variable
+      $options = isset($options) ? ['value' => $options, '_options_not_passed_as_array' => TRUE] : [];
+      @trigger_error('Passing any non-associative-array options to configure a constraint plugin is deprecated in drupal:11.3.0 and will not be supported in drupal:12.0.0. Use named arguments instead. See https://www.drupal.org/node/3522497', E_USER_DEPRECATED);
     }
     return $this->createInstance($name, $options);
   }
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AtLeastOneOfConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AtLeastOneOfConstraint.php
index de99cd9985ae..ab1d342c19d6 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AtLeastOneOfConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AtLeastOneOfConstraint.php
@@ -6,7 +6,6 @@
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
 use Symfony\Component\Validator\Constraints\AtLeastOneOf;
 
 /**
@@ -34,7 +33,7 @@ public static function create(ContainerInterface $container, array $configuratio
       }
     }
 
-    return new static($constraint_instances, [SymfonyConstraint::DEFAULT_GROUP]);
+    return new static($constraint_instances, [static::DEFAULT_GROUP]);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ClassResolverConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ClassResolverConstraint.php
index 677de9e73974..b072e1e7ae49 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ClassResolverConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ClassResolverConstraint.php
@@ -6,7 +6,6 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
 
 /**
  * Checks if a method on a service or instantiated object returns true.
@@ -26,7 +25,7 @@
   label: new TranslatableMarkup('Call a method on a service', [], ['context' => 'Validation']),
   type: FALSE,
 )]
-class ClassResolverConstraint extends SymfonyConstraint {
+class ClassResolverConstraint extends ConstraintBase {
 
   /**
    * The error message if validation fails.
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraint.php
index e009122ff139..afcad94a3bf8 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraint.php
@@ -4,7 +4,6 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
 
 /**
  * Complex data constraint.
@@ -15,7 +14,7 @@
   id: 'ComplexData',
   label: new TranslatableMarkup('Complex data', [], ['context' => 'Validation'])
 )]
-class ComplexDataConstraint extends SymfonyConstraint {
+class ComplexDataConstraint extends ConstraintBase {
 
   /**
    * An array of constraints for contained properties, keyed by property name.
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ConstraintBase.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ConstraintBase.php
new file mode 100644
index 000000000000..b0f030e00de4
--- /dev/null
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ConstraintBase.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Attribute\HasNamedArguments;
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * Base class for constraint plugins.
+ *
+ * This provides generic support for named option parameters passed to the
+ * class constructor.
+ */
+abstract class ConstraintBase extends Constraint {
+
+  #[HasNamedArguments]
+  public function __construct(...$args) {
+    if (!empty($args) && array_is_list($args)) {
+      @trigger_error(sprintf('Passing an array of options to configure the "%s" constraint is deprecated in drupal:11.3.0 and will not be supported in drupal:12.0.0. Use named arguments instead. See https://www.drupal.org/node/3522497', get_class($this)), E_USER_DEPRECATED);
+      parent::__construct(...$args);
+      return;
+    }
+
+    parent::__construct($args);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityBundleExistsConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityBundleExistsConstraint.php
index 61c2b443620a..d8d739416b98 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityBundleExistsConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityBundleExistsConstraint.php
@@ -6,7 +6,6 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
 
 /**
  * Checks if a bundle exists on a certain content entity type.
@@ -19,7 +18,7 @@
   label: new TranslatableMarkup('Entity bundle exists', [], ['context' => 'Validation']),
   type: 'entity'
 )]
-class EntityBundleExistsConstraint extends SymfonyConstraint {
+class EntityBundleExistsConstraint extends ConstraintBase {
 
   /**
    * The error message if validation fails.
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/FullyValidatableConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/FullyValidatableConstraint.php
index 4bd10c54f220..74021245ad79 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/FullyValidatableConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/FullyValidatableConstraint.php
@@ -6,7 +6,6 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
 
 /**
  * Constraint for fully validatable config schema type.
@@ -15,4 +14,4 @@
   id: 'FullyValidatable',
   label: new TranslatableMarkup('Whether this config schema type is fully validatable', [], ['context' => 'Validation'])
 )]
-final class FullyValidatableConstraint extends SymfonyConstraint {}
+final class FullyValidatableConstraint extends ConstraintBase {}
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php
index acdf03483c04..f8b6b12789fe 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php
@@ -4,7 +4,6 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
 
 /**
  * Supports validating all primitive types.
@@ -13,7 +12,7 @@
   id: 'PrimitiveType',
   label: new TranslatableMarkup('Primitive type', [], ['context' => 'Validation'])
 )]
-class PrimitiveTypeConstraint extends SymfonyConstraint {
+class PrimitiveTypeConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldConstraint.php
index db9f1dc1ec31..f496dd1ac387 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldConstraint.php
@@ -4,7 +4,6 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
 
 /**
  * Checks if an entity field has a unique value.
@@ -13,7 +12,7 @@
   id: 'UniqueField',
   label: new TranslatableMarkup('Unique field constraint', [], ['context' => 'Validation'])
 )]
-class UniqueFieldConstraint extends SymfonyConstraint {
+class UniqueFieldConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UriHostConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UriHostConstraint.php
index 81386205d0e1..327a7537db3a 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UriHostConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UriHostConstraint.php
@@ -6,7 +6,6 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
 
 /**
  * Checks if a string conforms to the RFC 3986 host component.
@@ -15,7 +14,7 @@
   id: 'UriHost',
   label: new TranslatableMarkup('URI host', [], ['context' => 'Validation']),
 )]
-class UriHostConstraint extends SymfonyConstraint {
+class UriHostConstraint extends ConstraintBase {
 
   /**
    * The error message if validation fails.
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ValidKeysConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ValidKeysConstraint.php
index 5236d9afe41a..c09bf54f0204 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ValidKeysConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ValidKeysConstraint.php
@@ -7,7 +7,6 @@
 use Drupal\Core\Config\Schema\Mapping;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
 use Symfony\Component\Validator\Context\ExecutionContextInterface;
 use Symfony\Component\Validator\Exception\InvalidArgumentException;
 
@@ -19,7 +18,7 @@
   label: new TranslatableMarkup('Valid mapping keys', [], ['context' => 'Validation']),
   type: ['mapping']
 )]
-class ValidKeysConstraint extends SymfonyConstraint {
+class ValidKeysConstraint extends ConstraintBase {
 
   /**
    * The error message if a key is invalid.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/CKEditor5ElementConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/CKEditor5ElementConstraint.php
index f303b5fc97b7..1b569c44ad78 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/CKEditor5ElementConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/CKEditor5ElementConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * CKEditor 5 element.
@@ -15,7 +15,7 @@
   id: 'CKEditor5Element',
   label: new TranslatableMarkup('CKEditor 5 element', [], ['context' => 'Validation'])
 )]
-class CKEditor5ElementConstraint extends SymfonyConstraint {
+class CKEditor5ElementConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/CKEditor5MediaAndFilterSettingsInSyncConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/CKEditor5MediaAndFilterSettingsInSyncConstraint.php
index b17747c3b7ce..7ea89976f701 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/CKEditor5MediaAndFilterSettingsInSyncConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/CKEditor5MediaAndFilterSettingsInSyncConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Ensure CKEditor 5 media plugin's and media filter's settings are in sync.
@@ -17,7 +17,7 @@
   id: 'CKEditor5MediaAndFilterSettingsInSync',
   label: new TranslatableMarkup('CKEditor 5 Media plugin in sync with filter settings', [], ['context' => 'Validation'])
 )]
-class CKEditor5MediaAndFilterSettingsInSyncConstraint extends SymfonyConstraint {
+class CKEditor5MediaAndFilterSettingsInSyncConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/EnabledConfigurablePluginsConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/EnabledConfigurablePluginsConstraint.php
index 3d646ed389d4..c3e77e59ec93 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/EnabledConfigurablePluginsConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/EnabledConfigurablePluginsConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * The CKEditor 5 plugin settings.
@@ -17,7 +17,7 @@
   id: 'CKEditor5EnabledConfigurablePlugins',
   label: new TranslatableMarkup('CKEditor 5 enabled configurable plugins', [], ['context' => 'Validation'])
 )]
-class EnabledConfigurablePluginsConstraint extends SymfonyConstraint {
+class EnabledConfigurablePluginsConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/FundamentalCompatibilityConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/FundamentalCompatibilityConstraint.php
index 866bb6c00ba5..fe0c2493c98e 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/FundamentalCompatibilityConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/FundamentalCompatibilityConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * The fundamental compatibility constraint.
@@ -17,7 +17,7 @@
   id: 'CKEditor5FundamentalCompatibility',
   label: new TranslatableMarkup('CKEditor 5 fundamental text format compatibility', [], ['context' => 'Validation'])
 )]
-class FundamentalCompatibilityConstraint extends SymfonyConstraint {
+class FundamentalCompatibilityConstraint extends ConstraintBase {
 
   /**
    * The violation message when no markup filters are enabled.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/SourceEditingPreventSelfXssConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/SourceEditingPreventSelfXssConstraint.php
index 96677c690966..8958234b46a3 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/SourceEditingPreventSelfXssConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/SourceEditingPreventSelfXssConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * For disallowing Source Editing configuration that allows self-XSS.
@@ -17,7 +17,7 @@
   id: 'SourceEditingPreventSelfXssConstraint',
   label: new TranslatableMarkup('Source Editing should never allow self-XSS.', [], ['context' => 'Validation'])
 )]
-class SourceEditingPreventSelfXssConstraint extends SymfonyConstraint {
+class SourceEditingPreventSelfXssConstraint extends ConstraintBase {
 
   /**
    * When Source Editing is configured to allow self-XSS.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/SourceEditingRedundantTagsConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/SourceEditingRedundantTagsConstraint.php
index 703f31e462ae..927c2112463c 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/SourceEditingRedundantTagsConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/SourceEditingRedundantTagsConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * For disallowing Source Editing elements already supported by a plugin.
@@ -17,7 +17,7 @@
   id: 'SourceEditingRedundantTags',
   label: new TranslatableMarkup('Source editing should only use otherwise unavailable tags and attributes', [], ['context' => 'Validation'])
 )]
-class SourceEditingRedundantTagsConstraint extends SymfonyConstraint {
+class SourceEditingRedundantTagsConstraint extends ConstraintBase {
 
   /**
    * When a Source Editing element is added that an enabled plugin supports.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/StyleSensibleElementConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/StyleSensibleElementConstraint.php
index 2161b827f072..212c4755ea85 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/StyleSensibleElementConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/StyleSensibleElementConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Styles can only be specified for HTML5 tags and extra classes.
@@ -17,7 +17,7 @@
   id: 'StyleSensibleElement',
   label: new TranslatableMarkup('Styles can only be specified for already supported tags.', [], ['context' => 'Validation'])
 )]
-class StyleSensibleElementConstraint extends SymfonyConstraint {
+class StyleSensibleElementConstraint extends ConstraintBase {
 
   /**
    * When a style is defined for a non-HTML5 tag.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemConditionsMetConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemConditionsMetConstraint.php
index 694595d06db2..8b43b0338a93 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemConditionsMetConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemConditionsMetConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * A (placed) CKEditor 5 toolbar item's conditions must be met.
@@ -17,7 +17,7 @@
   id: 'CKEditor5ToolbarItemConditionsMet',
   label: new TranslatableMarkup('CKEditor 5 toolbar item conditions must be met', [], ['context' => 'Validation'])
 )]
-class ToolbarItemConditionsMetConstraint extends SymfonyConstraint {
+class ToolbarItemConditionsMetConstraint extends ConstraintBase {
 
   /**
    * The violation message when the required image upload status is not set.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemConstraint.php
index 695b4d3e50e9..15cf96e87b4a 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * A CKEditor 5 toolbar item.
@@ -17,7 +17,7 @@
   id: 'CKEditor5ToolbarItem',
   label: new TranslatableMarkup('CKEditor 5 toolbar item', [], ['context' => 'Validation'])
 )]
-class ToolbarItemConstraint extends SymfonyConstraint {
+class ToolbarItemConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemDependencyConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemDependencyConstraint.php
index 1b2678b88641..c20b0a70eae6 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemDependencyConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/ToolbarItemDependencyConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * A CKEditor 5 toolbar item.
@@ -17,7 +17,7 @@
   id: 'CKEditor5ToolbarItemDependencyConstraint',
   label: new TranslatableMarkup('CKEditor 5 toolbar item dependency', [], ['context' => 'Validation'])
 )]
-class ToolbarItemDependencyConstraint extends SymfonyConstraint {
+class ToolbarItemDependencyConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/UniqueLabelInListConstraint.php b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/UniqueLabelInListConstraint.php
index e3fe8dca2f36..1f7485529b4a 100644
--- a/core/modules/ckeditor5/src/Plugin/Validation/Constraint/UniqueLabelInListConstraint.php
+++ b/core/modules/ckeditor5/src/Plugin/Validation/Constraint/UniqueLabelInListConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Uniquely labeled list item constraint.
@@ -17,7 +17,7 @@
   id: 'UniqueLabelInList',
   label: new TranslatableMarkup('Unique label in list', [], ['context' => 'Validation'])
 )]
-class UniqueLabelInListConstraint extends SymfonyConstraint {
+class UniqueLabelInListConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/content_moderation/src/Plugin/Validation/Constraint/ModerationStateConstraint.php b/core/modules/content_moderation/src/Plugin/Validation/Constraint/ModerationStateConstraint.php
index 9dd2b6ab9c0f..19cfa94020a5 100644
--- a/core/modules/content_moderation/src/Plugin/Validation/Constraint/ModerationStateConstraint.php
+++ b/core/modules/content_moderation/src/Plugin/Validation/Constraint/ModerationStateConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Verifies that nodes have a valid moderation state.
@@ -13,7 +13,7 @@
   id: 'ModerationState',
   label: new TranslatableMarkup('Valid moderation state', [], ['context' => 'Validation'])
 )]
-class ModerationStateConstraint extends SymfonyConstraint {
+class ModerationStateConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/content_translation/src/Plugin/Validation/Constraint/ContentTranslationSynchronizedFieldsConstraint.php b/core/modules/content_translation/src/Plugin/Validation/Constraint/ContentTranslationSynchronizedFieldsConstraint.php
index 77b1d060bf88..7c18cd6a97a0 100644
--- a/core/modules/content_translation/src/Plugin/Validation/Constraint/ContentTranslationSynchronizedFieldsConstraint.php
+++ b/core/modules/content_translation/src/Plugin/Validation/Constraint/ContentTranslationSynchronizedFieldsConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for the entity changed timestamp.
@@ -16,7 +16,7 @@
   label: new TranslatableMarkup('Content translation synchronized fields', [], ['context' => 'Validation']),
   type: ['entity']
 )]
-class ContentTranslationSynchronizedFieldsConstraint extends SymfonyConstraint {
+class ContentTranslationSynchronizedFieldsConstraint extends ConstraintBase {
 
   /**
    * Message shown for non-translatable field changes in non-default revision.
diff --git a/core/modules/datetime/src/Plugin/Validation/Constraint/DateTimeFormatConstraint.php b/core/modules/datetime/src/Plugin/Validation/Constraint/DateTimeFormatConstraint.php
index 610f39bfb6b3..c887caf8016e 100644
--- a/core/modules/datetime/src/Plugin/Validation/Constraint/DateTimeFormatConstraint.php
+++ b/core/modules/datetime/src/Plugin/Validation/Constraint/DateTimeFormatConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for DateTime items to ensure the format is correct.
@@ -13,7 +13,7 @@
   id: 'DateTimeFormat',
   label: new TranslatableMarkup('Datetime format valid for datetime type.', [], ['context' => 'Validation'])
 )]
-class DateTimeFormatConstraint extends SymfonyConstraint {
+class DateTimeFormatConstraint extends ConstraintBase {
 
   /**
    * Message for when the value isn't a string.
diff --git a/core/modules/file/src/Plugin/Validation/Constraint/FileEncodingConstraint.php b/core/modules/file/src/Plugin/Validation/Constraint/FileEncodingConstraint.php
index 5416fd760423..1628852eda99 100644
--- a/core/modules/file/src/Plugin/Validation/Constraint/FileEncodingConstraint.php
+++ b/core/modules/file/src/Plugin/Validation/Constraint/FileEncodingConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Defines an encoding constraint for files.
@@ -15,7 +15,7 @@
   id: 'FileEncoding',
   label: new TranslatableMarkup('File encoding', [], ['context' => 'Validation'])
 )]
-class FileEncodingConstraint extends SymfonyConstraint {
+class FileEncodingConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/modules/file/src/Plugin/Validation/Constraint/FileExtensionConstraint.php b/core/modules/file/src/Plugin/Validation/Constraint/FileExtensionConstraint.php
index 88e684283692..28221a3f09ad 100644
--- a/core/modules/file/src/Plugin/Validation/Constraint/FileExtensionConstraint.php
+++ b/core/modules/file/src/Plugin/Validation/Constraint/FileExtensionConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * File extension constraint.
@@ -16,7 +16,7 @@
   label: new TranslatableMarkup('File Extension', [], ['context' => 'Validation']),
   type: 'file'
 )]
-class FileExtensionConstraint extends SymfonyConstraint {
+class FileExtensionConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/modules/file/src/Plugin/Validation/Constraint/FileExtensionSecureConstraint.php b/core/modules/file/src/Plugin/Validation/Constraint/FileExtensionSecureConstraint.php
index 056c5fc393c5..661217ebca64 100644
--- a/core/modules/file/src/Plugin/Validation/Constraint/FileExtensionSecureConstraint.php
+++ b/core/modules/file/src/Plugin/Validation/Constraint/FileExtensionSecureConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * File extension secure constraint.
@@ -16,7 +16,7 @@
   label: new TranslatableMarkup('File Extension Secure', [], ['context' => 'Validation']),
   type: 'file'
 )]
-class FileExtensionSecureConstraint extends SymfonyConstraint {
+class FileExtensionSecureConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/modules/file/src/Plugin/Validation/Constraint/FileImageDimensionsConstraint.php b/core/modules/file/src/Plugin/Validation/Constraint/FileImageDimensionsConstraint.php
index 3fe13b9aedf8..8549d97c1cbc 100644
--- a/core/modules/file/src/Plugin/Validation/Constraint/FileImageDimensionsConstraint.php
+++ b/core/modules/file/src/Plugin/Validation/Constraint/FileImageDimensionsConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * File extension dimensions constraint.
@@ -16,7 +16,7 @@
   label: new TranslatableMarkup('File Image Dimensions', [], ['context' => 'Validation']),
   type: 'file'
 )]
-class FileImageDimensionsConstraint extends SymfonyConstraint {
+class FileImageDimensionsConstraint extends ConstraintBase {
 
   /**
    * The minimum dimensions.
diff --git a/core/modules/file/src/Plugin/Validation/Constraint/FileIsImageConstraint.php b/core/modules/file/src/Plugin/Validation/Constraint/FileIsImageConstraint.php
index a63a7470fbcb..0c85857e15c8 100644
--- a/core/modules/file/src/Plugin/Validation/Constraint/FileIsImageConstraint.php
+++ b/core/modules/file/src/Plugin/Validation/Constraint/FileIsImageConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * File is image constraint.
@@ -16,7 +16,7 @@
   label: new TranslatableMarkup('File Is Image', [], ['context' => 'Validation']),
   type: 'file'
 )]
-class FileIsImageConstraint extends SymfonyConstraint {
+class FileIsImageConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/modules/file/src/Plugin/Validation/Constraint/FileNameLengthConstraint.php b/core/modules/file/src/Plugin/Validation/Constraint/FileNameLengthConstraint.php
index cd541cc9495d..05f1f77ce797 100644
--- a/core/modules/file/src/Plugin/Validation/Constraint/FileNameLengthConstraint.php
+++ b/core/modules/file/src/Plugin/Validation/Constraint/FileNameLengthConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * File name length constraint.
@@ -14,7 +14,7 @@
   label: new TranslatableMarkup('File Name Length', [], ['context' => 'Validation']),
   type: 'file'
 )]
-class FileNameLengthConstraint extends SymfonyConstraint {
+class FileNameLengthConstraint extends ConstraintBase {
 
   /**
    * The maximum file name length.
diff --git a/core/modules/file/src/Plugin/Validation/Constraint/FileSizeLimitConstraint.php b/core/modules/file/src/Plugin/Validation/Constraint/FileSizeLimitConstraint.php
index 1b8ca0d20e03..63d62c9e17b9 100644
--- a/core/modules/file/src/Plugin/Validation/Constraint/FileSizeLimitConstraint.php
+++ b/core/modules/file/src/Plugin/Validation/Constraint/FileSizeLimitConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * File size max constraint.
@@ -14,7 +14,7 @@
   label: new TranslatableMarkup('File Size Limit', [], ['context' => 'Validation']),
   type: 'file'
 )]
-class FileSizeLimitConstraint extends SymfonyConstraint {
+class FileSizeLimitConstraint extends ConstraintBase {
 
   /**
    * The message for when file size limit is exceeded.
diff --git a/core/modules/file/src/Plugin/Validation/Constraint/FileValidationConstraint.php b/core/modules/file/src/Plugin/Validation/Constraint/FileValidationConstraint.php
index 2ed9c9f884e7..e44d4583ec09 100644
--- a/core/modules/file/src/Plugin/Validation/Constraint/FileValidationConstraint.php
+++ b/core/modules/file/src/Plugin/Validation/Constraint/FileValidationConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation File constraint.
@@ -13,6 +13,6 @@
   id: 'FileValidation',
   label: new TranslatableMarkup('File Validation', [], ['context' => 'Validation'])
 )]
-class FileValidationConstraint extends SymfonyConstraint {
+class FileValidationConstraint extends ConstraintBase {
 
 }
diff --git a/core/modules/image/tests/modules/image_field_property_constraint_validation/src/Plugin/Validation/Constraint/AltTextContainsLlamasConstraint.php b/core/modules/image/tests/modules/image_field_property_constraint_validation/src/Plugin/Validation/Constraint/AltTextContainsLlamasConstraint.php
index 2fe6847eff14..96c95bb5c26e 100644
--- a/core/modules/image/tests/modules/image_field_property_constraint_validation/src/Plugin/Validation/Constraint/AltTextContainsLlamasConstraint.php
+++ b/core/modules/image/tests/modules/image_field_property_constraint_validation/src/Plugin/Validation/Constraint/AltTextContainsLlamasConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Provides a Contains Llamas constraint.
@@ -15,7 +15,7 @@
   id: 'AltTextContainsLlamas',
   label: new TranslatableMarkup('Contains Llamas', options: ['context' => 'Validation'])
 )]
-final class AltTextContainsLlamasConstraint extends SymfonyConstraint {
+final class AltTextContainsLlamasConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkAccessConstraint.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkAccessConstraint.php
index 18fe46c4d55f..86c05941f149 100644
--- a/core/modules/link/src/Plugin/Validation/Constraint/LinkAccessConstraint.php
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkAccessConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Defines an access validation constraint for links.
@@ -13,7 +13,7 @@
   id: 'LinkAccess',
   label: new TranslatableMarkup('Link URI can be accessed by the user.', [], ['context' => 'Validation'])
 )]
-class LinkAccessConstraint extends SymfonyConstraint {
+class LinkAccessConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraint.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraint.php
index bd383c0c4eb2..ebf4f8644909 100644
--- a/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraint.php
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Defines a protocol validation constraint for links to external URLs.
@@ -13,7 +13,7 @@
   id: 'LinkExternalProtocols',
   label: new TranslatableMarkup('No dangerous external protocols', [], ['context' => 'Validation'])
 )]
-class LinkExternalProtocolsConstraint extends SymfonyConstraint {
+class LinkExternalProtocolsConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraint.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraint.php
index 6d25cf00a25f..7bd8686433b5 100644
--- a/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraint.php
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Defines a protocol validation constraint for links to broken internal URLs.
@@ -13,7 +13,7 @@
   id: 'LinkNotExistingInternal',
   label: new TranslatableMarkup('No broken internal links', [], ['context' => 'Validation'])
 )]
-class LinkNotExistingInternalConstraint extends SymfonyConstraint {
+class LinkNotExistingInternalConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
index 83333afdaad2..eb77dbaff9e3 100644
--- a/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for links receiving data allowed by its settings.
@@ -13,7 +13,7 @@
   id: 'LinkType',
   label: new TranslatableMarkup('Link data valid for link type.', [], ['context' => 'Validation'])
 )]
-class LinkTypeConstraint extends SymfonyConstraint {
+class LinkTypeConstraint extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/modules/media/src/Plugin/Validation/Constraint/MediaMappingsConstraint.php b/core/modules/media/src/Plugin/Validation/Constraint/MediaMappingsConstraint.php
index 17a2e6df4081..5f948e22e2c9 100644
--- a/core/modules/media/src/Plugin/Validation/Constraint/MediaMappingsConstraint.php
+++ b/core/modules/media/src/Plugin/Validation/Constraint/MediaMappingsConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\Validation\Attribute\Constraint;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validates media mappings.
@@ -16,7 +16,7 @@
   label: new TranslatableMarkup('Media Mapping Constraint', [], ['context' => 'Validation']),
   type: 'string'
 )]
-class MediaMappingsConstraint extends SymfonyConstraint {
+class MediaMappingsConstraint extends ConstraintBase {
 
   /**
    * The error message if source is used in media mapping.
diff --git a/core/modules/media/src/Plugin/Validation/Constraint/OEmbedResourceConstraint.php b/core/modules/media/src/Plugin/Validation/Constraint/OEmbedResourceConstraint.php
index dcde3c870956..1b7a78835840 100644
--- a/core/modules/media/src/Plugin/Validation/Constraint/OEmbedResourceConstraint.php
+++ b/core/modules/media/src/Plugin/Validation/Constraint/OEmbedResourceConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks if a value represents a valid oEmbed resource URL.
@@ -18,7 +18,7 @@
   label: new TranslatableMarkup('oEmbed resource', [], ['context' => 'Validation']),
   type: ['link', 'string', 'string_long']
 )]
-class OEmbedResourceConstraint extends SymfonyConstraint {
+class OEmbedResourceConstraint extends ConstraintBase {
 
   /**
    * The error message if the URL does not match any known provider.
diff --git a/core/modules/media/tests/modules/media_test_source/src/Plugin/Validation/Constraint/MediaTestConstraint.php b/core/modules/media/tests/modules/media_test_source/src/Plugin/Validation/Constraint/MediaTestConstraint.php
index 50290eb9b036..894a9dc17232 100644
--- a/core/modules/media/tests/modules/media_test_source/src/Plugin/Validation/Constraint/MediaTestConstraint.php
+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/Validation/Constraint/MediaTestConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * A media test constraint.
@@ -16,7 +16,7 @@
   label: new TranslatableMarkup('Media constraint for test purposes.', [], ['context' => 'Validation']),
   type: ['entity', 'string']
 )]
-class MediaTestConstraint extends SymfonyConstraint {
+class MediaTestConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/menu_ui/src/Plugin/Validation/Constraint/MenuSettingsConstraint.php b/core/modules/menu_ui/src/Plugin/Validation/Constraint/MenuSettingsConstraint.php
index 0a379096e0ac..b7d05a66cc87 100644
--- a/core/modules/menu_ui/src/Plugin/Validation/Constraint/MenuSettingsConstraint.php
+++ b/core/modules/menu_ui/src/Plugin/Validation/Constraint/MenuSettingsConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for changing the menu settings in pending revisions.
@@ -13,7 +13,7 @@
   id: 'MenuSettings',
   label: new TranslatableMarkup('Menu settings.', [], ['context' => 'Validation'])
 )]
-class MenuSettingsConstraint extends SymfonyConstraint {
+class MenuSettingsConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/path/src/Plugin/Validation/Constraint/PathAliasConstraint.php b/core/modules/path/src/Plugin/Validation/Constraint/PathAliasConstraint.php
index 636aeeca101d..e8ae960661fc 100644
--- a/core/modules/path/src/Plugin/Validation/Constraint/PathAliasConstraint.php
+++ b/core/modules/path/src/Plugin/Validation/Constraint/PathAliasConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for changing path aliases in pending revisions.
@@ -13,7 +13,7 @@
   id: 'PathAlias',
   label: new TranslatableMarkup('Path alias.', [], ['context' => 'Validation'])
 )]
-class PathAliasConstraint extends SymfonyConstraint {
+class PathAliasConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/rest/tests/modules/rest_test/src/Plugin/Validation/Constraint/RestTestConstraint.php b/core/modules/rest/tests/modules/rest_test/src/Plugin/Validation/Constraint/RestTestConstraint.php
index 9c9e94afe7a5..db0ccc7aa05f 100644
--- a/core/modules/rest/tests/modules/rest_test/src/Plugin/Validation/Constraint/RestTestConstraint.php
+++ b/core/modules/rest/tests/modules/rest_test/src/Plugin/Validation/Constraint/RestTestConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Adds some validations for a REST test field.
@@ -17,7 +17,7 @@
   id: 'rest_test_validation',
   label: new TranslatableMarkup('REST test validation', [], ['context' => 'Validation'])
 )]
-class RestTestConstraint extends SymfonyConstraint {
+class RestTestConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/EntityTestEntityLevel.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/EntityTestEntityLevel.php
index 956c032b5d59..a1bba9062f17 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/EntityTestEntityLevel.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/EntityTestEntityLevel.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Constraint on entity level.
@@ -16,7 +16,7 @@
   label: new TranslatableMarkup('Constraint on the entity level.'),
   type: ['entity']
 )]
-class EntityTestEntityLevel extends SymfonyConstraint {
+class EntityTestEntityLevel extends ConstraintBase {
 
   /**
    * The error message.
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/FieldWidgetConstraint.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/FieldWidgetConstraint.php
index 88f3be980aa2..c542967df511 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/FieldWidgetConstraint.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/FieldWidgetConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Supports validating widget constraints.
@@ -15,7 +15,7 @@
   id: 'FieldWidgetConstraint',
   label: new TranslatableMarkup('Field widget constraint.')
 )]
-class FieldWidgetConstraint extends SymfonyConstraint {
+class FieldWidgetConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/TestValidatedReferenceConstraint.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/TestValidatedReferenceConstraint.php
index 180799237ff5..5e678d777d75 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/TestValidatedReferenceConstraint.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/TestValidatedReferenceConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validates referenced entities.
@@ -15,7 +15,7 @@
   id: 'TestValidatedReferenceConstraint',
   label: new TranslatableMarkup('Test validated reference constraint.')
 )]
-class TestValidatedReferenceConstraint extends SymfonyConstraint {
+class TestValidatedReferenceConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/ProtectedUserFieldConstraint.php b/core/modules/user/src/Plugin/Validation/Constraint/ProtectedUserFieldConstraint.php
index bb112e0a593a..a98e5a735a4b 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/ProtectedUserFieldConstraint.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/ProtectedUserFieldConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks if the plain text password is provided for editing a protected field.
@@ -13,7 +13,7 @@
   id: 'ProtectedUserField',
   label: new TranslatableMarkup('Password required for protected field change', [], ['context' => 'Validation'])
 )]
-class ProtectedUserFieldConstraint extends SymfonyConstraint {
+class ProtectedUserFieldConstraint extends ConstraintBase {
 
   /**
    * Violation message.
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraint.php b/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraint.php
index 790cfc0f2ba7..31f5181d8965 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraint.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/RoleExistsConstraint.php
@@ -6,7 +6,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks if a role exists.
@@ -15,7 +15,7 @@
   id: 'RoleExists',
   label: new TranslatableMarkup('Role exists', [], ['context' => 'Validation'])
 )]
-class RoleExistsConstraint extends SymfonyConstraint {
+class RoleExistsConstraint extends ConstraintBase {
 
   /**
    * The error message if validation fails.
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php
index 404ba73e8617..40c5de6ff65e 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks if the user's email address is provided if required.
@@ -17,7 +17,7 @@
   id: 'UserMailRequired',
   label: new TranslatableMarkup('User email required', [], ['context' => 'Validation'])
 )]
-class UserMailRequired extends SymfonyConstraint {
+class UserMailRequired extends ConstraintBase {
 
   /**
    * Violation message. Use the same message as FormValidator.
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraint.php b/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraint.php
index 8739f0a3f8c6..4455a3f4b7d9 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraint.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Checks if a value is a valid user name.
@@ -13,7 +13,7 @@
   id: 'UserName',
   label: new TranslatableMarkup('User name', [], ['context' => 'Validation'])
 )]
-class UserNameConstraint extends SymfonyConstraint {
+class UserNameConstraint extends ConstraintBase {
 
   /**
    * The violation message when there is no username.
diff --git a/core/modules/workspaces/src/Plugin/Validation/Constraint/DeletedWorkspaceConstraint.php b/core/modules/workspaces/src/Plugin/Validation/Constraint/DeletedWorkspaceConstraint.php
index ecc1c1393d91..bc6e80635fd8 100644
--- a/core/modules/workspaces/src/Plugin/Validation/Constraint/DeletedWorkspaceConstraint.php
+++ b/core/modules/workspaces/src/Plugin/Validation/Constraint/DeletedWorkspaceConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Deleted workspace constraint.
@@ -13,7 +13,7 @@
   id: 'DeletedWorkspace',
   label: new TranslatableMarkup('Deleted workspace', [], ['context' => 'Validation'])
 )]
-class DeletedWorkspaceConstraint extends SymfonyConstraint {
+class DeletedWorkspaceConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityReferenceSupportedNewEntitiesConstraint.php b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityReferenceSupportedNewEntitiesConstraint.php
index 81df97812b6c..71015ab2e732 100644
--- a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityReferenceSupportedNewEntitiesConstraint.php
+++ b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityReferenceSupportedNewEntitiesConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * The entity reference supported new entities constraint.
@@ -13,7 +13,7 @@
   id: 'EntityReferenceSupportedNewEntities',
   label: new TranslatableMarkup('Entity Reference Supported New Entities', [], ['context' => 'Validation'])
 )]
-class EntityReferenceSupportedNewEntitiesConstraint extends SymfonyConstraint {
+class EntityReferenceSupportedNewEntitiesConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
diff --git a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraint.php b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraint.php
index 3b21fe55e056..7b0aab6f2f26 100644
--- a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraint.php
+++ b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraint.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
-use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\ConstraintBase;
 
 /**
  * Validation constraint for an entity being edited in multiple workspaces.
@@ -14,7 +14,7 @@
   label: new TranslatableMarkup('Entity workspace conflict', [], ['context' => 'Validation']),
   type: ['entity']
 )]
-class EntityWorkspaceConflictConstraint extends SymfonyConstraint {
+class EntityWorkspaceConflictConstraint extends ConstraintBase {
 
   /**
    * The default violation message.
-- 
GitLab


From 8ec55887cbecbb2476b456e2b292dce648c1c99b Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Sat, 31 May 2025 13:18:35 -0700
Subject: [PATCH 11/18] Fix test.

---
 .../Plugin/Validation/Constraint/PluginExistsConstraint.php   | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php b/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
index 6b7599d6d01c..2ed2661f9059 100644
--- a/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
+++ b/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
@@ -70,7 +70,9 @@ class PluginExistsConstraint extends ConstraintBase implements ContainerFactoryP
    *   Domain-specific data attached to a constraint.
    */
   public function __construct(public readonly PluginManagerInterface $pluginManager, mixed $options = NULL, ?array $groups = NULL, mixed $payload = NULL) {
-    parent::__construct($options, $groups, $payload);
+    $options['groups'] = $groups;
+    $options['payload'] = $payload;
+    parent::__construct(...$options);
   }
 
   /**
-- 
GitLab


From e55288746b464f1f83ea89e571c076c09c400946 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Sat, 31 May 2025 13:51:14 -0700
Subject: [PATCH 12/18] Fix tests 2.

---
 .../Drupal/Core/Entity/TypedData/EntityDataDefinition.php | 8 ++++----
 core/lib/Drupal/Core/Validation/ConstraintManager.php     | 4 +++-
 .../Context/EntityContextDefinitionIsSatisfiedTest.php    | 8 ++++----
 3 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php b/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php
index 0bf4966dd9ff..3aaeabe88c27 100644
--- a/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php
+++ b/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php
@@ -117,21 +117,21 @@ public function getDataType() {
    * {@inheritdoc}
    */
   public function getEntityTypeId() {
-    return $this->definition['constraints']['EntityType'] ?? NULL;
+    return $this->definition['constraints']['EntityType']['type'] ?? NULL;
   }
 
   /**
    * {@inheritdoc}
    */
   public function setEntityTypeId($entity_type_id) {
-    return $this->addConstraint('EntityType', $entity_type_id);
+    return $this->addConstraint('EntityType', ['type' => $entity_type_id]);
   }
 
   /**
    * {@inheritdoc}
    */
   public function getBundles() {
-    $bundle = $this->definition['constraints']['Bundle'] ?? NULL;
+    $bundle = $this->definition['constraints']['Bundle']['bundle'] ?? NULL;
     return is_string($bundle) ? [$bundle] : $bundle;
   }
 
@@ -140,7 +140,7 @@ public function getBundles() {
    */
   public function setBundles(?array $bundles = NULL) {
     if (isset($bundles)) {
-      $this->addConstraint('Bundle', $bundles);
+      $this->addConstraint('Bundle', ['bundle' => $bundles]);
     }
     else {
       // Remove the constraint.
diff --git a/core/lib/Drupal/Core/Validation/ConstraintManager.php b/core/lib/Drupal/Core/Validation/ConstraintManager.php
index f94e7788e716..36cf1fc394e5 100644
--- a/core/lib/Drupal/Core/Validation/ConstraintManager.php
+++ b/core/lib/Drupal/Core/Validation/ConstraintManager.php
@@ -79,12 +79,14 @@ protected function getDiscovery() {
    */
   public function create($name, $options) {
     if (!is_array($options)) {
+      if ($options !== NULL) {
+        @trigger_error('Passing any non-associative-array options to configure a constraint plugin is deprecated in drupal:11.3.0 and will not be supported in drupal:12.0.0. Use named arguments instead. See https://www.drupal.org/node/3522497', E_USER_DEPRECATED);
+      }
       // Plugins need an array as configuration, so make sure we have one.
       // The constraint classes support passing the options as part of the
       // 'value' key also.
       // @phpstan-ignore isset.variable
       $options = isset($options) ? ['value' => $options, '_options_not_passed_as_array' => TRUE] : [];
-      @trigger_error('Passing any non-associative-array options to configure a constraint plugin is deprecated in drupal:11.3.0 and will not be supported in drupal:12.0.0. Use named arguments instead. See https://www.drupal.org/node/3522497', E_USER_DEPRECATED);
     }
     return $this->createInstance($name, $options);
   }
diff --git a/core/tests/Drupal/Tests/Core/Plugin/Context/EntityContextDefinitionIsSatisfiedTest.php b/core/tests/Drupal/Tests/Core/Plugin/Context/EntityContextDefinitionIsSatisfiedTest.php
index 6a1a14e49469..50d2f7969b32 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/Context/EntityContextDefinitionIsSatisfiedTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/Context/EntityContextDefinitionIsSatisfiedTest.php
@@ -163,7 +163,7 @@ public static function providerTestIsSatisfiedBy() {
     $data['content entity, incorrect manual constraint'] = [
       TRUE,
       EntityContextDefinition::fromEntityType($content),
-      EntityContextDefinition::fromEntityType($content)->addConstraint('EntityType', 'test_config'),
+      EntityContextDefinition::fromEntityType($content)->addConstraint('EntityType', ['type' =>'test_config']),
     ];
     $data['config entity, matching type, no value'] = [
       TRUE,
@@ -235,9 +235,9 @@ public function testIsSatisfiedByGenerateBundledEntity($expected, array $require
 
     $requirement = EntityContextDefinition::fromEntityType($entity_type);
     if ($requirement_bundles) {
-      $requirement->addConstraint('Bundle', $requirement_bundles);
+      $requirement->addConstraint('Bundle', ['bundle' => $requirement_bundles]);
     }
-    $definition = EntityContextDefinition::fromEntityType($entity_type)->addConstraint('Bundle', $candidate_bundles);
+    $definition = EntityContextDefinition::fromEntityType($entity_type)->addConstraint('Bundle', ['bundle' => $candidate_bundles]);
     $this->assertRequirementIsSatisfied($expected, $requirement, $definition);
   }
 
@@ -313,7 +313,7 @@ public function testIsSatisfiedByPassBundledEntity($expected, $requirement_const
 
     $requirement = EntityContextDefinition::fromEntityTypeId('test_content');
     if ($requirement_constraint) {
-      $requirement->addConstraint('Bundle', $requirement_constraint);
+      $requirement->addConstraint('Bundle', ['bundle' => $requirement_constraint]);
     }
     $definition = EntityContextDefinition::fromEntityTypeId('test_content');
     $this->assertRequirementIsSatisfied($expected, $requirement, $definition, $entity->reveal());
-- 
GitLab


From b42359251fa89ed8615fd47e5a83400bf323a184 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Sat, 31 May 2025 13:58:04 -0700
Subject: [PATCH 13/18] PHPCS.

---
 .../Plugin/Context/EntityContextDefinitionIsSatisfiedTest.php   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/tests/Drupal/Tests/Core/Plugin/Context/EntityContextDefinitionIsSatisfiedTest.php b/core/tests/Drupal/Tests/Core/Plugin/Context/EntityContextDefinitionIsSatisfiedTest.php
index 50d2f7969b32..0ad8050b28fe 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/Context/EntityContextDefinitionIsSatisfiedTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/Context/EntityContextDefinitionIsSatisfiedTest.php
@@ -163,7 +163,7 @@ public static function providerTestIsSatisfiedBy() {
     $data['content entity, incorrect manual constraint'] = [
       TRUE,
       EntityContextDefinition::fromEntityType($content),
-      EntityContextDefinition::fromEntityType($content)->addConstraint('EntityType', ['type' =>'test_config']),
+      EntityContextDefinition::fromEntityType($content)->addConstraint('EntityType', ['type' => 'test_config']),
     ];
     $data['config entity, matching type, no value'] = [
       TRUE,
-- 
GitLab


From 24a2efba87fa22d4c9e40c21c328aca510cef719 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Sat, 31 May 2025 14:25:26 -0700
Subject: [PATCH 14/18] Fix tests 3.

---
 core/config/schema/core.data_types.schema.yml | 24 ++++++++++++-------
 core/config/schema/core.entity.schema.yml     |  6 +++--
 .../Core/Config/Entity/ConfigEntityType.php   |  2 +-
 .../Core/Entity/Entity/EntityFormDisplay.php  | 10 ++++----
 .../Core/Entity/Entity/EntityFormMode.php     |  6 +++--
 .../Core/Entity/Entity/EntityViewDisplay.php  | 10 ++++----
 .../Core/Entity/Entity/EntityViewMode.php     |  6 +++--
 .../Core/Field/Entity/BaseFieldOverride.php   | 12 ++++++----
 core/modules/field/src/Entity/FieldConfig.php | 12 ++++++----
 .../field/src/Entity/FieldStorageConfig.php   | 10 ++++----
 .../config/schema/language.schema.yml         |  3 ++-
 .../src/Entity/ContentLanguageSettings.php    |  8 ++++---
 .../OverridesSectionStorage.php               |  2 +-
 core/modules/media/src/Entity/MediaType.php   |  6 +++--
 .../config/schema/navigation.schema.yml       |  6 +++--
 .../Config/ConfigEntityValidationTestBase.php |  6 ++---
 .../Core/Config/ConfigSchemaTest.php          | 16 ++++++-------
 ...ityBundleExistsConstraintValidatorTest.php | 10 ++++----
 .../EntityHasFieldConstraintValidatorTest.php |  2 +-
 ...tablePropertiesConstraintValidatorTest.php |  8 +++----
 ...ExtensionExistsConstraintValidatorTest.php |  6 ++---
 .../ValidKeysConstraintValidatorTest.php      |  2 +-
 22 files changed, 102 insertions(+), 71 deletions(-)

diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index cb67da15333d..2a8b5add092e 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -53,7 +53,8 @@ mapping:
   constraints:
     # By default, allow the explicitly listed mapping keys, and require their
     # presence unless `requiredKey: false` is specified.
-    ValidKeys: '<infer>'
+    ValidKeys:
+      allowedKeys: '<infer>'
 sequence:
   label: Sequence
   class: '\Drupal\Core\Config\Schema\Sequence'
@@ -199,7 +200,8 @@ _core_config_info:
         Length:
           exactly: 43
   constraints:
-    ValidKeys: ['default_config_hash']
+    ValidKeys:
+      allowedKeys: ['default_config_hash']
 
 config_object:
   type: mapping
@@ -375,7 +377,8 @@ config_dependencies_base:
         constraints:
           NotBlank: []
           ExtensionName: []
-          ExtensionExists: module
+          ExtensionExists:
+            type: module
     theme:
       # All dependency keys are optional: this might not depend on any themes.
       requiredKey: false
@@ -386,9 +389,11 @@ config_dependencies_base:
         constraints:
           NotBlank: []
           ExtensionName: []
-          ExtensionExists: theme
+          ExtensionExists:
+            type: theme
   constraints:
-    ValidKeys: '<infer>'
+    ValidKeys:
+      allowedKeys: '<infer>'
 
 config_dependencies:
   type: config_dependencies_base
@@ -400,7 +405,8 @@ config_dependencies:
       type: config_dependencies_base
       label: 'Enforced configuration dependencies'
   constraints:
-    ValidKeys: '<infer>'
+    ValidKeys:
+      allowedKeys: '<infer>'
 
 config_entity:
   type: mapping
@@ -455,7 +461,8 @@ block_settings:
       constraints:
         NotBlank: []
         ExtensionName: []
-        ExtensionExists: module
+        ExtensionExists:
+          type: module
     context_mapping:
       requiredKey: false
       type: sequence
@@ -549,7 +556,8 @@ field_config_base:
       type: string
       label: 'Bundle'
       constraints:
-        EntityBundleExists: '%parent.entity_type'
+        EntityBundleExists:
+          entityTypeId: '%parent.entity_type'
     label:
       type: required_label
       label: 'Label'
diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index 47f9674785f0..dc7865f6044c 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -72,7 +72,8 @@ core.entity_view_display.*.*.*:
       type: string
       label: 'Bundle'
       constraints:
-        EntityBundleExists: '%parent.targetEntityType'
+        EntityBundleExists:
+          entityTypeId: '%parent.targetEntityType'
     mode:
       type: string
       label: 'View or form mode machine name'
@@ -139,7 +140,8 @@ core.entity_form_display.*.*.*:
       type: string
       label: 'Bundle'
       constraints:
-        EntityBundleExists: '%parent.targetEntityType'
+        EntityBundleExists:
+          entityTypeId: '%parent.targetEntityType'
     mode:
       type: string
       label: 'View or form mode machine name'
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php
index 3b15335a205f..e084b0a400ad 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityType.php
@@ -200,7 +200,7 @@ public function getConstraints() {
     $id_key = $this->getKey('id');
     if ($id_key) {
       $constraints += [
-        'ImmutableProperties' => [$id_key],
+        'ImmutableProperties' => ['properties' => [$id_key]],
       ];
     }
     return $constraints;
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
index 4034dd1ee09e..51d8444a0fd5 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php
@@ -33,10 +33,12 @@
   ],
   constraints: [
     'ImmutableProperties' => [
-      'id',
-      'targetEntityType',
-      'bundle',
-      'mode',
+      'properties' => [
+        'id',
+        'targetEntityType',
+        'bundle',
+        'mode',
+      ],
     ],
   ],
   config_export: [
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityFormMode.php b/core/lib/Drupal/Core/Entity/Entity/EntityFormMode.php
index b84d5c05a970..f21ac4d7a0f2 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityFormMode.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityFormMode.php
@@ -34,8 +34,10 @@
   ],
   constraints: [
     'ImmutableProperties' => [
-      'id',
-      'targetEntityType',
+      'properties' => [
+        'id',
+        'targetEntityType',
+      ],
     ],
   ],
   config_export: [
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
index 8ee4318a8df1..475dc9a251c7 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
@@ -32,10 +32,12 @@
   ],
   constraints: [
     'ImmutableProperties' => [
-      'id',
-      'targetEntityType',
-      'bundle',
-      'mode',
+      'properties' => [
+        'id',
+        'targetEntityType',
+        'bundle',
+        'mode',
+      ],
     ],
   ],
   config_export: [
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityViewMode.php b/core/lib/Drupal/Core/Entity/Entity/EntityViewMode.php
index e1427520e1ec..b4bc1bd6ebfc 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityViewMode.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewMode.php
@@ -36,8 +36,10 @@
   ],
   constraints: [
     'ImmutableProperties' => [
-      'id',
-      'targetEntityType',
+      'properties' => [
+        'id',
+        'targetEntityType',
+      ],
     ],
   ],
   config_export: [
diff --git a/core/lib/Drupal/Core/Field/Entity/BaseFieldOverride.php b/core/lib/Drupal/Core/Field/Entity/BaseFieldOverride.php
index 0f28e265203b..00f5e4909a5e 100644
--- a/core/lib/Drupal/Core/Field/Entity/BaseFieldOverride.php
+++ b/core/lib/Drupal/Core/Field/Entity/BaseFieldOverride.php
@@ -30,11 +30,13 @@
   ],
   constraints: [
     'ImmutableProperties' => [
-      'id',
-      'entity_type',
-      'bundle',
-      'field_name',
-      'field_type',
+      'properties' => [
+        'id',
+        'entity_type',
+        'bundle',
+        'field_name',
+        'field_type',
+      ],
     ],
   ],
   config_export: [
diff --git a/core/modules/field/src/Entity/FieldConfig.php b/core/modules/field/src/Entity/FieldConfig.php
index c27ca0ef272c..f8cb1641496f 100644
--- a/core/modules/field/src/Entity/FieldConfig.php
+++ b/core/modules/field/src/Entity/FieldConfig.php
@@ -38,11 +38,13 @@
   constraints: [
     'RequiredConfigDependencies' => ['field_storage_config'],
     'ImmutableProperties' => [
-      'id',
-      'entity_type',
-      'field_name',
-      'bundle',
-      'field_type',
+      'properties' => [
+        'id',
+        'entity_type',
+        'field_name',
+        'bundle',
+        'field_type',
+      ],
     ],
   ],
   config_export: [
diff --git a/core/modules/field/src/Entity/FieldStorageConfig.php b/core/modules/field/src/Entity/FieldStorageConfig.php
index b9cca0f1fa23..656692d437a5 100644
--- a/core/modules/field/src/Entity/FieldStorageConfig.php
+++ b/core/modules/field/src/Entity/FieldStorageConfig.php
@@ -41,10 +41,12 @@
   ],
   constraints: [
     'ImmutableProperties' => [
-      'id',
-      'entity_type',
-      'field_name',
-      'type',
+      'properties' => [
+        'id',
+        'entity_type',
+        'field_name',
+        'type',
+      ],
     ],
   ],
   config_export: [
diff --git a/core/modules/language/config/schema/language.schema.yml b/core/modules/language/config/schema/language.schema.yml
index f7ddf9f08584..f3bf7bbd9d35 100644
--- a/core/modules/language/config/schema/language.schema.yml
+++ b/core/modules/language/config/schema/language.schema.yml
@@ -138,7 +138,8 @@ language.content_settings.*.*:
       type:  string
       label: 'Bundle'
       constraints:
-        EntityBundleExists: '%parent.target_entity_type_id'
+        EntityBundleExists:
+          entityTypeId: '%parent.target_entity_type_id'
     default_langcode:
       type: langcode
       label: 'Default language'
diff --git a/core/modules/language/src/Entity/ContentLanguageSettings.php b/core/modules/language/src/Entity/ContentLanguageSettings.php
index 16506f04b26d..ce5b61904df8 100644
--- a/core/modules/language/src/Entity/ContentLanguageSettings.php
+++ b/core/modules/language/src/Entity/ContentLanguageSettings.php
@@ -34,9 +34,11 @@
   ],
   constraints: [
     'ImmutableProperties' => [
-      'id',
-      'target_entity_type_id',
-      'target_bundle',
+      'properties' => [
+        'id',
+        'target_entity_type_id',
+        'target_bundle',
+      ],
     ],
   ],
   config_export: [
diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
index 7fc7491fb85b..7e002530f28f 100644
--- a/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
+++ b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
@@ -44,7 +44,7 @@
       data_type: 'entity',
       label: new TranslatableMarkup("Entity"),
       constraints: [
-        "EntityHasField" => OverridesSectionStorage::FIELD_NAME,
+        "EntityHasField" => ['field_name' => OverridesSectionStorage::FIELD_NAME],
       ],
     ),
     'view_mode' => new ContextDefinition(
diff --git a/core/modules/media/src/Entity/MediaType.php b/core/modules/media/src/Entity/MediaType.php
index b2026cc2dd8b..b4716c248f66 100644
--- a/core/modules/media/src/Entity/MediaType.php
+++ b/core/modules/media/src/Entity/MediaType.php
@@ -59,8 +59,10 @@
   ],
   constraints: [
     'ImmutableProperties' => [
-      'id',
-      'source',
+      'properties' => [
+        'id',
+        'source',
+      ],
     ],
     'MediaMappingsConstraint' => [],
   ],
diff --git a/core/modules/navigation/config/schema/navigation.schema.yml b/core/modules/navigation/config/schema/navigation.schema.yml
index ee31849202a0..a83eca34eb96 100644
--- a/core/modules/navigation/config/schema/navigation.schema.yml
+++ b/core/modules/navigation/config/schema/navigation.schema.yml
@@ -49,9 +49,11 @@ navigation.settings:
                 Range:
                   min: 0
           constraints:
-            ValidKeys: '<infer>'
+            ValidKeys:
+              allowedKeys: '<infer>'
       constraints:
-        ValidKeys: '<infer>'
+        ValidKeys:
+          allowedKeys: '<infer>'
 
 navigation.block_layout:
   type: config_object
diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigEntityValidationTestBase.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigEntityValidationTestBase.php
index cb0b3086b141..bfb04761d181 100644
--- a/core/tests/Drupal/KernelTests/Core/Config/ConfigEntityValidationTestBase.php
+++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigEntityValidationTestBase.php
@@ -472,9 +472,9 @@ public function testLangcode(): void {
    */
   public function testImmutableProperties(array $valid_values = []): void {
     $constraints = $this->entity->getEntityType()->getConstraints();
-    $this->assertNotEmpty($constraints['ImmutableProperties'], 'All config entities should have at least one immutable ID property.');
+    $this->assertNotEmpty($constraints['ImmutableProperties']['properties'], 'All config entities should have at least one immutable ID property.');
 
-    foreach ($constraints['ImmutableProperties'] as $property_name) {
+    foreach ($constraints['ImmutableProperties']['properties'] as $property_name) {
       $original_value = $this->entity->get($property_name);
       $this->entity->set($property_name, $valid_values[$property_name] ?? $this->randomMachineName());
       $this->assertValidationErrors([
@@ -561,7 +561,7 @@ public function testRequiredPropertyValuesMissing(?array $additional_expected_va
 
     // Get the config entity properties that are immutable.
     // @see ::testImmutableProperties()
-    $immutable_properties = $this->entity->getEntityType()->getConstraints()['ImmutableProperties'];
+    $immutable_properties = $this->entity->getEntityType()->getConstraints()['ImmutableProperties']['properties'];
 
     // Config entity properties containing plugin collections are special cases:
     // setting them to NULL would cause them to get out of sync with the plugin
diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
index 4cd56888088f..a768abaa287b 100644
--- a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
@@ -79,7 +79,7 @@ public function testSchemaMapping(): void {
     $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
     $expected['unwrap_for_canonical_representation'] = TRUE;
     $expected['constraints'] = [
-      'ValidKeys' => '<infer>',
+      'ValidKeys' => ['allowedKeys' => '<infer>'],
       'LangcodeRequiredIfTranslatableValues' => NULL,
     ];
     $this->assertEquals($expected, $definition, 'Retrieved the right metadata for configuration with only some schema.');
@@ -132,7 +132,7 @@ public function testSchemaMapping(): void {
     $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
     $expected['unwrap_for_canonical_representation'] = TRUE;
     $expected['constraints'] = [
-      'ValidKeys' => '<infer>',
+      'ValidKeys' => ['allowedKeys' => '<infer>'],
       'FullyValidatable' => NULL,
       'LangcodeRequiredIfTranslatableValues' => NULL,
     ];
@@ -169,7 +169,7 @@ public function testSchemaMapping(): void {
     $expected['type'] = 'config_schema_test.ignore';
     $expected['unwrap_for_canonical_representation'] = TRUE;
     $expected['constraints'] = [
-      'ValidKeys' => '<infer>',
+      'ValidKeys' => ['allowedKeys' => '<infer>'],
       'LangcodeRequiredIfTranslatableValues' => NULL,
     ];
 
@@ -226,7 +226,7 @@ public function testSchemaMapping(): void {
     $expected['mapping']['_core']['requiredKey'] = FALSE;
     $expected['type'] = 'image.style.*';
     $expected['constraints'] = [
-      'ValidKeys' => '<infer>',
+      'ValidKeys' => ['allowedKeys' => '<infer>'],
       'FullyValidatable' => NULL,
     ];
 
@@ -252,7 +252,7 @@ public function testSchemaMapping(): void {
     $expected['mapping']['upscale']['label'] = 'Upscale';
     $expected['type'] = 'image.effect.image_scale';
     $expected['constraints'] = [
-      'ValidKeys' => '<infer>',
+      'ValidKeys' => ['allowedKeys' => '<infer>'],
       'FullyValidatable' => NULL,
     ];
 
@@ -284,7 +284,7 @@ public function testSchemaMapping(): void {
       'integer' => ['type' => 'integer', 'requiredKey' => TRUE],
       'string' => ['type' => 'string', 'requiredKey' => TRUE],
     ];
-    $expected['constraints'] = ['ValidKeys' => '<infer>'];
+    $expected['constraints'] = ['ValidKeys' => ['allowedKeys' => '<infer>']];
     $this->assertEquals($expected, $definition, 'Retrieved the right metadata for config_test.dynamic.third_party:third_party_settings.config_schema_test');
 
     // More complex, several level deep test.
@@ -306,7 +306,7 @@ public function testSchemaMapping(): void {
     $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
     $expected['unwrap_for_canonical_representation'] = TRUE;
     $expected['constraints'] = [
-      'ValidKeys' => '<infer>',
+      'ValidKeys' => ['allowedKeys' => '<infer>'],
       'LangcodeRequiredIfTranslatableValues' => NULL,
     ];
 
@@ -590,7 +590,7 @@ public function testSchemaFallback(): void {
     $expected['mapping']['test_description']['label'] = 'Description';
     $expected['type'] = 'config_schema_test.wildcard_fallback.*';
     $expected['constraints'] = [
-      'ValidKeys' => '<infer>',
+      'ValidKeys' => ['allowedKeys' => '<infer>'],
       'LangcodeRequiredIfTranslatableValues' => NULL,
     ];
 
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityBundleExistsConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityBundleExistsConstraintValidatorTest.php
index 1981091fafb4..237291df3a77 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityBundleExistsConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityBundleExistsConstraintValidatorTest.php
@@ -41,7 +41,7 @@ protected function setUp(): void {
    */
   public function testValueMustBeAString(): void {
     $definition = DataDefinition::create('any')
-      ->addConstraint('EntityBundleExists', 'entity_test_with_bundle');
+      ->addConstraint('EntityBundleExists', ['entityTypeId' => 'entity_test_with_bundle']);
 
     $this->expectException(UnexpectedTypeException::class);
     $this->expectExceptionMessage('Expected argument of type "string", "int" given');
@@ -55,7 +55,7 @@ public function testValueMustBeAString(): void {
    */
   public function testEntityTypeIdIsStatic(): void {
     $definition = DataDefinition::create('string')
-      ->addConstraint('EntityBundleExists', 'entity_test_with_bundle');
+      ->addConstraint('EntityBundleExists', ['entityTypeId' => 'entity_test_with_bundle']);
 
     $violations = $this->container->get('typed_data_manager')
       ->create($definition, 'bar')
@@ -84,7 +84,7 @@ public function testDynamicEntityType(string $constraint_value, string $resolved
 
     $this->assertStringStartsWith('%', $constraint_value);
     $value_definition = DataDefinition::create('string')
-      ->addConstraint('EntityBundleExists', $constraint_value);
+      ->addConstraint('EntityBundleExists', ['entityTypeId' => $constraint_value]);
 
     $parent_definition = MapDataDefinition::create()
       ->setPropertyDefinition('entity_type_id', DataDefinition::create('string'))
@@ -109,7 +109,7 @@ public function testEntityTypeIdFromMultipleParents(): void {
       )
       ->setPropertyDefinition('info2', MapDataDefinition::create()
         ->setPropertyDefinition('bundle', DataDefinition::create('string')
-          ->addConstraint('EntityBundleExists', '%parent.%parent.info.entity_type_id')
+          ->addConstraint('EntityBundleExists', ['entityTypeId' => '%parent.%parent.info.entity_type_id'])
         )
       );
 
@@ -134,7 +134,7 @@ public function testEntityTypeIdFromMultipleParents(): void {
   public function testInvalidEntityTypeId(): void {
     $entity_type_id = $this->randomMachineName();
     $definition = DataDefinition::create('string')
-      ->addConstraint('EntityBundleExists', $entity_type_id);
+      ->addConstraint('EntityBundleExists', ['entityTypeId' => $entity_type_id]);
 
     $violations = $this->container->get('typed_data_manager')
       ->create($definition, 'bar')
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityHasFieldConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityHasFieldConstraintValidatorTest.php
index 7f717646e874..26865b64cfc4 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityHasFieldConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityHasFieldConstraintValidatorTest.php
@@ -33,7 +33,7 @@ protected function setUp(): void {
    */
   public function testValidation(): void {
     $this->state->set('entity_test_constraints.build', [
-      'EntityHasField' => 'body',
+      'EntityHasField' => ['field_name' => 'body'],
     ]);
 
     /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ImmutablePropertiesConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ImmutablePropertiesConstraintValidatorTest.php
index be935baf42a7..6e25341a9055 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/ImmutablePropertiesConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/ImmutablePropertiesConstraintValidatorTest.php
@@ -31,7 +31,7 @@ class ImmutablePropertiesConstraintValidatorTest extends KernelTestBase {
    */
   public function testValidatorRequiresAConfigEntity(): void {
     $definition = DataDefinition::createFromDataType('any')
-      ->addConstraint('ImmutableProperties', ['read_only']);
+      ->addConstraint('ImmutableProperties', ['properties' => ['read_only']]);
     $data = $this->container->get(TypedDataManagerInterface::class)
       ->create($definition, 39);
     $this->expectException(UnexpectedValueException::class);
@@ -52,7 +52,7 @@ public function testValidatorRejectsANonExistentProperty(): void {
     $this->assertFalse(property_exists($entity, 'non_existent'));
 
     $definition = DataDefinition::createFromDataType('entity:block_content_type')
-      ->addConstraint('ImmutableProperties', ['non_existent']);
+      ->addConstraint('ImmutableProperties', ['properties' => ['non_existent']]);
 
     $this->expectException(LogicException::class);
     $this->expectExceptionMessage("The entity does not have a 'non_existent' property.");
@@ -71,7 +71,7 @@ public function testValidatedEntityMustHaveAnId(): void {
     $entity->id()->shouldBeCalled();
 
     $definition = DataDefinition::createFromDataType('any')
-      ->addConstraint('ImmutableProperties', ['read_only']);
+      ->addConstraint('ImmutableProperties', ['properties' => ['read_only']]);
     $data = $this->container->get(TypedDataManagerInterface::class)
       ->create($definition, $entity->reveal());
     $this->expectException(LogicException::class);
@@ -91,7 +91,7 @@ public function testImmutablePropertyCannotBeChanged(): void {
     $entity->save();
 
     $definition = DataDefinition::createFromDataType('entity:block_content_type')
-      ->addConstraint('ImmutableProperties', ['id', 'description']);
+      ->addConstraint('ImmutableProperties', ['properties' => ['id', 'description']]);
 
     /** @var \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager */
     $typed_data_manager = $this->container->get(TypedDataManagerInterface::class);
diff --git a/core/tests/Drupal/KernelTests/Core/Extension/ExtensionExistsConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Extension/ExtensionExistsConstraintValidatorTest.php
index 6868cd519c8c..993debd20484 100644
--- a/core/tests/Drupal/KernelTests/Core/Extension/ExtensionExistsConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Extension/ExtensionExistsConstraintValidatorTest.php
@@ -29,7 +29,7 @@ public function testValidation(): void {
     // Create a data definition that specifies the value must be a string with
     // the name of an installed module.
     $definition = DataDefinition::create('string')
-      ->addConstraint('ExtensionExists', 'module');
+      ->addConstraint('ExtensionExists', ['type' => 'module']);
 
     /** @var \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data */
     $typed_data = $this->container->get('typed_data_manager');
@@ -52,7 +52,7 @@ public function testValidation(): void {
     $data->setValue(NULL);
     $this->assertCount(0, $data->validate());
 
-    $definition->setConstraints(['ExtensionExists' => 'theme']);
+    $definition->setConstraints(['ExtensionExists' => ['type' => 'theme']]);
     $data = $typed_data->create($definition, 'stark');
 
     $violations = $data->validate();
@@ -80,7 +80,7 @@ public function testValidation(): void {
     $this->assertCount(0, $data->validate());
 
     // Anything but a module or theme should raise an exception.
-    $definition->setConstraints(['ExtensionExists' => 'profile']);
+    $definition->setConstraints(['ExtensionExists' => ['type' => 'profile']]);
     $this->expectExceptionMessage("Unknown extension type: 'profile'");
     $data->validate();
   }
diff --git a/core/tests/Drupal/KernelTests/Core/TypedData/ValidKeysConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/TypedData/ValidKeysConstraintValidatorTest.php
index 5026321001a9..867e38f22cbb 100644
--- a/core/tests/Drupal/KernelTests/Core/TypedData/ValidKeysConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/TypedData/ValidKeysConstraintValidatorTest.php
@@ -326,7 +326,7 @@ public function testValidKeyInference(): void {
     $config = $this->container->get('config.typed')
       ->get('system.site');
     $config->getDataDefinition()
-      ->addConstraint('ValidKeys', '<infer>');
+      ->addConstraint('ValidKeys', ['allowedKeys' => '<infer>'],);
 
     $data = $config->getValue();
     $data['invalid-key'] = "There's a snake in my boots.";
-- 
GitLab


From 9be10be6b0bb9ced8376e8fcf401143d63dbad1c Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Sat, 31 May 2025 15:49:16 -0700
Subject: [PATCH 15/18] Fix tests 4.

---
 .../Drupal/Core/Field/BaseFieldDefinition.php  |  6 +++---
 core/lib/Drupal/Core/Field/FieldConfigBase.php |  4 ++--
 .../Field/Plugin/Field/FieldType/EmailItem.php | 16 +++++++++-------
 .../Plugin/Field/FieldType/IntegerItem.php     | 16 +++++++++-------
 .../Plugin/Field/FieldType/LanguageItem.php    |  8 +++++---
 .../Plugin/Field/FieldType/NumericItemBase.php | 10 ++++++----
 .../Plugin/Field/FieldType/StringItem.php      |  6 ++++--
 .../Plugin/Field/FieldType/TimestampItem.php   | 10 ++++++----
 .../Core/Validation/ConstraintManager.php      |  2 +-
 .../Constraint/ComplexDataConstraint.php       | 15 ++++++++++-----
 .../block/config/schema/block.schema.yml       |  3 ++-
 .../src/Plugin/Field/FieldType/TestItem.php    | 18 ++++++++++--------
 .../Plugin/Field/FieldType/TelephoneItem.php   | 10 ++++++----
 .../src/Plugin/Field/FieldType/TextItem.php    | 10 ++++++----
 .../Field/FieldType/TextWithSummaryItem.php    |  8 +++++---
 .../ComplexDataConstraintValidatorTest.php     | 14 +++++++++-----
 16 files changed, 93 insertions(+), 63 deletions(-)

diff --git a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
index d8a96660c966..012e8d48049f 100644
--- a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
+++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
@@ -305,7 +305,7 @@ public function isMultiple() {
    */
   public function setPropertyConstraints($name, array $constraints) {
     $item_constraints = $this->getItemDefinition()->getConstraints();
-    $item_constraints['ComplexData'][$name] = $constraints;
+    $item_constraints['ComplexData']['properties'][$name] = $constraints;
     $this->getItemDefinition()->setConstraints($item_constraints);
     return $this;
   }
@@ -343,7 +343,7 @@ public function setPropertyConstraints($name, array $constraints) {
    * @see \Drupal\Core\Field\BaseFieldDefinition::addConstraint()
    */
   public function addPropertyConstraints($name, array $constraints) {
-    $item_constraints = $this->getItemDefinition()->getConstraint('ComplexData') ?: [];
+    $item_constraints = $this->getItemDefinition()->getConstraint('ComplexData')['properties'] ?? [];
     if (isset($item_constraints[$name])) {
       // Add the new property constraints, overwriting as required.
       $item_constraints[$name] = $constraints + $item_constraints[$name];
@@ -351,7 +351,7 @@ public function addPropertyConstraints($name, array $constraints) {
     else {
       $item_constraints[$name] = $constraints;
     }
-    $this->getItemDefinition()->addConstraint('ComplexData', $item_constraints);
+    $this->getItemDefinition()->addConstraint('ComplexData', ['properties' => $item_constraints]);
     return $this;
   }
 
diff --git a/core/lib/Drupal/Core/Field/FieldConfigBase.php b/core/lib/Drupal/Core/Field/FieldConfigBase.php
index bab688eac427..09d796fb88d5 100644
--- a/core/lib/Drupal/Core/Field/FieldConfigBase.php
+++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
@@ -557,7 +557,7 @@ public function getItemDefinition() {
         ->setSettings($this->getSettings());
 
       // Add any custom property constraints, overwriting as required.
-      $item_constraints = $this->itemDefinition->getConstraint('ComplexData') ?: [];
+      $item_constraints = $this->itemDefinition->getConstraint('ComplexData')['properties'] ?? [];
       foreach ($this->propertyConstraints as $name => $constraints) {
         if (isset($item_constraints[$name])) {
           $item_constraints[$name] = $constraints + $item_constraints[$name];
@@ -565,7 +565,7 @@ public function getItemDefinition() {
         else {
           $item_constraints[$name] = $constraints;
         }
-        $this->itemDefinition->addConstraint('ComplexData', $item_constraints);
+        $this->itemDefinition->addConstraint('ComplexData', ['properties' => $item_constraints]);
       }
     }
 
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
index 33c8849f3f95..0c38aee7a4c4 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
@@ -56,13 +56,15 @@ public function getConstraints() {
     $constraints = parent::getConstraints();
 
     $constraints[] = $constraint_manager->create('ComplexData', [
-      'value' => [
-        'Length' => [
-          'max' => Email::EMAIL_MAX_LENGTH,
-          'maxMessage' => $this->t('%name: the email address can not be longer than @max characters.', [
-            '%name' => $this->getFieldDefinition()->getLabel(),
-            '@max' => Email::EMAIL_MAX_LENGTH,
-          ]),
+      'properties' => [
+        'value' => [
+          'Length' => [
+            'max' => Email::EMAIL_MAX_LENGTH,
+            'maxMessage' => $this->t('%name: the email address can not be longer than @max characters.', [
+              '%name' => $this->getFieldDefinition()->getLabel(),
+              '@max' => Email::EMAIL_MAX_LENGTH,
+            ]),
+          ],
         ],
       ],
     ]);
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
index a3125b2ea531..b99a420f0873 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
@@ -71,13 +71,15 @@ public function getConstraints() {
     if ($this->getSetting('unsigned')) {
       $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
       $constraints[] = $constraint_manager->create('ComplexData', [
-        'value' => [
-          'Range' => [
-            'min' => 0,
-            'minMessage' => $this->t('%name: The integer must be larger or equal to %min.', [
-              '%name' => $this->getFieldDefinition()->getLabel(),
-              '%min' => 0,
-            ]),
+        'properties' =>[
+          'value' => [
+            'Range' => [
+              'min' => 0,
+              'minMessage' => $this->t('%name: The integer must be larger or equal to %min.', [
+                '%name' => $this->getFieldDefinition()->getLabel(),
+                '%min' => 0,
+              ]),
+            ],
           ],
         ],
       ]);
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
index 99357d4574d1..e7f4a5a2aec7 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php
@@ -25,8 +25,10 @@
   no_ui: TRUE,
   constraints: [
     "ComplexData" => [
-      "value" => [
-        "Length" => ["max" => 12],
+      "properties" => [
+        "value" => [
+          "Length" => ["max" => 12],
+        ],
       ],
     ],
   ]
@@ -112,7 +114,7 @@ public function onChange($property_name, $notify = TRUE) {
    */
   public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
     // Defer to the callback in the item definition as it can be overridden.
-    $constraint = $field_definition->getItemDefinition()->getConstraint('ComplexData');
+    $constraint = $field_definition->getItemDefinition()->getConstraint('ComplexData')['properties'] ?? [];
     if (isset($constraint['value']['AllowedValues']['callback'])) {
       $languages = call_user_func($constraint['value']['AllowedValues']['callback']);
     }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php
index fb1bea28c5cd..c8a5c1b16298 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php
@@ -82,10 +82,12 @@ public function getConstraints() {
     if (isset($settings['min']) && $settings['min'] !== '') {
       $min = $settings['min'];
       $constraints[] = $constraint_manager->create('ComplexData', [
-        'value' => [
-          'Range' => [
-            'min' => $min,
-            'minMessage' => $this->t('%name: the value may be no less than %min.', ['%name' => $label, '%min' => $min]),
+        'properties' => [
+          'value' => [
+            'Range' => [
+              'min' => $min,
+              'minMessage' => $this->t('%name: the value may be no less than %min.', ['%name' => $label, '%min' => $min]),
+            ],
           ],
         ],
       ]);
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
index cbe14fab7378..c19e98b880bf 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
@@ -68,8 +68,10 @@ public function getConstraints() {
         ]);
       }
       $constraints[] = $constraint_manager->create('ComplexData', [
-        'value' => [
-          'Length' => $options,
+        'properties' =>[
+          'value' => [
+            'Length' => $options,
+          ],
         ],
       ]);
     }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php
index 79d7353894bd..d412df89b622 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php
@@ -25,10 +25,12 @@
   default_formatter: "timestamp",
   constraints: [
     "ComplexData" => [
-      "value" => [
-        "Range" => [
-          "min" => "-2147483648",
-          "max" => "2147483648",
+      'properties' => [
+        "value" => [
+          "Range" => [
+            "min" => "-2147483648",
+            "max" => "2147483648",
+          ],
         ],
       ],
     ],
diff --git a/core/lib/Drupal/Core/Validation/ConstraintManager.php b/core/lib/Drupal/Core/Validation/ConstraintManager.php
index 36cf1fc394e5..703f9dc292fa 100644
--- a/core/lib/Drupal/Core/Validation/ConstraintManager.php
+++ b/core/lib/Drupal/Core/Validation/ConstraintManager.php
@@ -80,7 +80,7 @@ protected function getDiscovery() {
   public function create($name, $options) {
     if (!is_array($options)) {
       if ($options !== NULL) {
-        @trigger_error('Passing any non-associative-array options to configure a constraint plugin is deprecated in drupal:11.3.0 and will not be supported in drupal:12.0.0. Use named arguments instead. See https://www.drupal.org/node/3522497', E_USER_DEPRECATED);
+        @trigger_error(sprintf('Passing any non-associative-array options to configure constraint plugin "%s" is deprecated in drupal:11.3.0 and will not be supported in drupal:12.0.0. Use named arguments instead. See https://www.drupal.org/node/3522497', $name), E_USER_DEPRECATED);
       }
       // Plugins need an array as configuration, so make sure we have one.
       // The constraint classes support passing the options as part of the
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraint.php
index afcad94a3bf8..ec358972b2b0 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraint.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Validation\Attribute\Constraint;
+use Symfony\Component\Validator\Attribute\HasNamedArguments;
 
 /**
  * Complex data constraint.
@@ -26,12 +27,16 @@ class ComplexDataConstraint extends ConstraintBase {
   /**
    * {@inheritdoc}
    */
-  public function __construct($options = NULL) {
-    // Allow skipping the 'properties' key in the options.
-    if (is_array($options) && !array_key_exists('properties', $options)) {
-      $options = ['properties' => $options];
+  #[HasNamedArguments]
+  public function __construct(...$args) {
+    if (!empty($args) && array_is_list($args)) {
+      // Allow skipping the 'properties' key in the options.
+      if (!array_key_exists('properties', $args[0])) {
+        $args[0] = ['properties' => $args[0]];
+      }
     }
-    parent::__construct($options);
+
+    parent::__construct(...$args);
     $constraint_manager = \Drupal::service('validation.constraint');
 
     // Instantiate constraint objects for array definitions.
diff --git a/core/modules/block/config/schema/block.schema.yml b/core/modules/block/config/schema/block.schema.yml
index f9c8f9927391..391e2091fbcc 100644
--- a/core/modules/block/config/schema/block.schema.yml
+++ b/core/modules/block/config/schema/block.schema.yml
@@ -22,7 +22,8 @@ block.block.*:
       constraints:
         NotBlank: []
         ExtensionName: []
-        ExtensionExists: theme
+        ExtensionExists:
+          type: theme
     region:
       type: string
       label: 'Region'
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
index 9e89b21e9e77..1f596b216caa 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
@@ -126,14 +126,16 @@ public function getConstraints() {
     $constraints = parent::getConstraints();
 
     $constraints[] = $constraint_manager->create('ComplexData', [
-      'value' => [
-        'TestField' => [
-          'value' => -1,
-          'message' => $this->t('%name does not accept the value @value.', [
-            '%name' => $this->getFieldDefinition()
-              ->getLabel(),
-            '@value' => -1,
-          ]),
+      'properties' => [
+        'value' => [
+          'TestField' => [
+            'value' => -1,
+            'message' => $this->t('%name does not accept the value @value.', [
+              '%name' => $this->getFieldDefinition()
+                ->getLabel(),
+              '@value' => -1,
+            ]),
+          ],
         ],
       ],
     ]);
diff --git a/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php b/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
index dd97b961d14d..a913e834f170 100644
--- a/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
+++ b/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
@@ -67,10 +67,12 @@ public function getConstraints() {
     $constraints = parent::getConstraints();
 
     $constraints[] = $constraint_manager->create('ComplexData', [
-      'value' => [
-        'Length' => [
-          'max' => self::MAX_LENGTH,
-          'maxMessage' => $this->t('%name: the telephone number may not be longer than @max characters.', ['%name' => $this->getFieldDefinition()->getLabel(), '@max' => self::MAX_LENGTH]),
+      'properties' => [
+        'value' => [
+          'Length' => [
+            'max' => self::MAX_LENGTH,
+            'maxMessage' => $this->t('%name: the telephone number may not be longer than @max characters.', ['%name' => $this->getFieldDefinition()->getLabel(), '@max' => self::MAX_LENGTH]),
+          ],
         ],
       ],
     ]);
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
index 5d3ac034b346..956fc0fd5dcd 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
@@ -65,10 +65,12 @@ public function getConstraints() {
 
     if ($max_length = $this->getSetting('max_length')) {
       $constraints[] = $constraint_manager->create('ComplexData', [
-        'value' => [
-          'Length' => [
-            'max' => $max_length,
-            'maxMessage' => $this->t('%name: the text may not be longer than @max characters.', ['%name' => $this->getFieldDefinition()->getLabel(), '@max' => $max_length]),
+        'properties' => [
+          'value' => [
+            'Length' => [
+              'max' => $max_length,
+              'maxMessage' => $this->t('%name: the text may not be longer than @max characters.', ['%name' => $this->getFieldDefinition()->getLabel(), '@max' => $max_length]),
+            ],
           ],
         ],
       ]);
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
index f099a53a93c2..caaba40b4b4c 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
@@ -121,9 +121,11 @@ public function getConstraints() {
     if ($this->getSetting('required_summary')) {
       $manager = $this->getTypedDataManager()->getValidationConstraintManager();
       $constraints[] = $manager->create('ComplexData', [
-        'summary' => [
-          'NotNull' => [
-            'message' => $this->t('The summary field is required for @name', ['@name' => $this->getFieldDefinition()->getLabel()]),
+        'properties' =>[
+          'summary' => [
+            'NotNull' => [
+              'message' => $this->t('The summary field is required for @name', ['@name' => $this->getFieldDefinition()->getLabel()]),
+            ],
           ],
         ],
       ]);
diff --git a/core/tests/Drupal/KernelTests/Core/TypedData/ComplexDataConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/TypedData/ComplexDataConstraintValidatorTest.php
index 17934095205a..fc63d25e610b 100644
--- a/core/tests/Drupal/KernelTests/Core/TypedData/ComplexDataConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/TypedData/ComplexDataConstraintValidatorTest.php
@@ -40,9 +40,11 @@ public function testValidation(): void {
     $definition = MapDataDefinition::create()
       ->setPropertyDefinition('key', DataDefinition::create('integer'))
       ->addConstraint('ComplexData', [
-        'key' => [
-          'AllowedValues' => [1, 2, 3],
-        ],
+        'properties' => [
+          'key' => [
+            'AllowedValues' => [1, 2, 3],
+          ],
+        ]
       ]);
 
     // Test the validation.
@@ -70,8 +72,10 @@ public function testValidation(): void {
     $definition = MapDataDefinition::create()
       ->setPropertyDefinition('key', DataDefinition::create('integer'))
       ->addConstraint('ComplexData', [
-        'key' => [
-          'NotNull' => [],
+        'properties' => [
+          'key' => [
+            'NotNull' => [],
+          ],
         ],
       ]);
 
-- 
GitLab


From 505eb324c19c5a456a6503f779e913cda310c0f0 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Sat, 31 May 2025 15:56:58 -0700
Subject: [PATCH 16/18] PHPCS.

---
 .../Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php    | 2 +-
 .../lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php | 2 +-
 .../text/src/Plugin/Field/FieldType/TextWithSummaryItem.php     | 2 +-
 .../Core/TypedData/ComplexDataConstraintValidatorTest.php       | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
index b99a420f0873..820c70118b17 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
@@ -71,7 +71,7 @@ public function getConstraints() {
     if ($this->getSetting('unsigned')) {
       $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
       $constraints[] = $constraint_manager->create('ComplexData', [
-        'properties' =>[
+        'properties' => [
           'value' => [
             'Range' => [
               'min' => 0,
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
index c19e98b880bf..54d832827e37 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
@@ -68,7 +68,7 @@ public function getConstraints() {
         ]);
       }
       $constraints[] = $constraint_manager->create('ComplexData', [
-        'properties' =>[
+        'properties' => [
           'value' => [
             'Length' => $options,
           ],
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
index caaba40b4b4c..a280501fd360 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
@@ -121,7 +121,7 @@ public function getConstraints() {
     if ($this->getSetting('required_summary')) {
       $manager = $this->getTypedDataManager()->getValidationConstraintManager();
       $constraints[] = $manager->create('ComplexData', [
-        'properties' =>[
+        'properties' => [
           'summary' => [
             'NotNull' => [
               'message' => $this->t('The summary field is required for @name', ['@name' => $this->getFieldDefinition()->getLabel()]),
diff --git a/core/tests/Drupal/KernelTests/Core/TypedData/ComplexDataConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/TypedData/ComplexDataConstraintValidatorTest.php
index fc63d25e610b..0a263cb9e935 100644
--- a/core/tests/Drupal/KernelTests/Core/TypedData/ComplexDataConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/TypedData/ComplexDataConstraintValidatorTest.php
@@ -44,7 +44,7 @@ public function testValidation(): void {
           'key' => [
             'AllowedValues' => [1, 2, 3],
           ],
-        ]
+        ],
       ]);
 
     // Test the validation.
-- 
GitLab


From 9a6ae1ff5bd86b857a4e9e4fe816cd32699d5863 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Sat, 31 May 2025 17:35:36 -0700
Subject: [PATCH 17/18] Fix tests 5.

---
 .../Field/FieldType/EntityReferenceItem.php      |  2 +-
 .../Plugin/Field/FieldType/NumericItemBase.php   | 16 +++++++++-------
 .../Constraint/PluginExistsConstraint.php        |  8 ++++++--
 core/modules/editor/src/Entity/Editor.php        |  2 +-
 core/modules/field/src/Entity/FieldConfig.php    |  2 +-
 .../Entity/EntityTypeConstraintValidatorTest.php |  2 +-
 .../PluginExistsConstraintValidatorTest.php      |  2 +-
 .../ValidKeysConstraintValidatorTest.php         |  4 ++--
 8 files changed, 22 insertions(+), 16 deletions(-)

diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
index e614255f5186..a5c1f0b1685d 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -105,7 +105,7 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
       // We can add a constraint for the target entity type. The list of
       // referenceable bundles is a field setting, so the corresponding
       // constraint is added dynamically in ::getConstraints().
-      ->addConstraint('EntityType', $settings['target_type']);
+      ->addConstraint('EntityType', ['type' => $settings['target_type']]);
 
     return $properties;
   }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php
index c8a5c1b16298..c26eceef2f11 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php
@@ -96,13 +96,15 @@ public function getConstraints() {
     if (isset($settings['max']) && $settings['max'] !== '') {
       $max = $settings['max'];
       $constraints[] = $constraint_manager->create('ComplexData', [
-        'value' => [
-          'Range' => [
-            'max' => $max,
-            'maxMessage' => $this->t('%name: the value may be no greater than %max.', [
-              '%name' => $label,
-              '%max' => $max,
-            ]),
+        'properties' => [
+          'value' => [
+            'Range' => [
+              'max' => $max,
+              'maxMessage' => $this->t('%name: the value may be no greater than %max.', [
+                '%name' => $label,
+                '%max' => $max,
+              ]),
+            ],
           ],
         ],
       ]);
diff --git a/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php b/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
index 2ed2661f9059..96eafc9067fd 100644
--- a/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
+++ b/core/lib/Drupal/Core/Plugin/Plugin/Validation/Constraint/PluginExistsConstraint.php
@@ -70,8 +70,12 @@ class PluginExistsConstraint extends ConstraintBase implements ContainerFactoryP
    *   Domain-specific data attached to a constraint.
    */
   public function __construct(public readonly PluginManagerInterface $pluginManager, mixed $options = NULL, ?array $groups = NULL, mixed $payload = NULL) {
-    $options['groups'] = $groups;
-    $options['payload'] = $payload;
+    if ($groups) {
+      $options['groups'] = $groups;
+    }
+    if ($payload) {
+      $options['payload'] = $payload;
+    }
     parent::__construct(...$options);
   }
 
diff --git a/core/modules/editor/src/Entity/Editor.php b/core/modules/editor/src/Entity/Editor.php
index 8dbf85868bc6..13183d170549 100644
--- a/core/modules/editor/src/Entity/Editor.php
+++ b/core/modules/editor/src/Entity/Editor.php
@@ -36,7 +36,7 @@
   ],
   constraints: [
     'RequiredConfigDependencies' => [
-      'filter_format',
+      'entityTypes' => ['filter_format'],
     ],
   ],
   config_export: [
diff --git a/core/modules/field/src/Entity/FieldConfig.php b/core/modules/field/src/Entity/FieldConfig.php
index f8cb1641496f..05c70b90ed64 100644
--- a/core/modules/field/src/Entity/FieldConfig.php
+++ b/core/modules/field/src/Entity/FieldConfig.php
@@ -36,7 +36,7 @@
     'plural' => '@count fields',
   ],
   constraints: [
-    'RequiredConfigDependencies' => ['field_storage_config'],
+    'RequiredConfigDependencies' => ['entityTypes' => ['field_storage_config']],
     'ImmutableProperties' => [
       'properties' => [
         'id',
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityTypeConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityTypeConstraintValidatorTest.php
index b13215255d27..b47019fb8429 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityTypeConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityTypeConstraintValidatorTest.php
@@ -41,7 +41,7 @@ public function testValidation(): void {
     $entity_type = 'node';
     $definition = DataDefinition::create('entity_reference')
       ->setConstraints([
-        'EntityType' => $entity_type,
+        'EntityType' => ['type' => $entity_type],
       ]
     );
 
diff --git a/core/tests/Drupal/KernelTests/Core/Plugin/PluginExistsConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Plugin/PluginExistsConstraintValidatorTest.php
index 9b37a142ae06..fcde5e478c4b 100644
--- a/core/tests/Drupal/KernelTests/Core/Plugin/PluginExistsConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Plugin/PluginExistsConstraintValidatorTest.php
@@ -30,7 +30,7 @@ class PluginExistsConstraintValidatorTest extends KernelTestBase {
    */
   public function testValidation(): void {
     $definition = DataDefinition::create('string')
-      ->addConstraint('PluginExists', 'plugin.manager.action');
+      ->addConstraint('PluginExists', ['manager' => 'plugin.manager.action']);
 
     // An existing action plugin should pass validation.
     $data = $this->container->get('typed_data_manager')->create($definition);
diff --git a/core/tests/Drupal/KernelTests/Core/TypedData/ValidKeysConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/TypedData/ValidKeysConstraintValidatorTest.php
index 867e38f22cbb..80cb3abd2aa6 100644
--- a/core/tests/Drupal/KernelTests/Core/TypedData/ValidKeysConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/TypedData/ValidKeysConstraintValidatorTest.php
@@ -228,7 +228,7 @@ public function testBothUnknownAndDynamicallyRequiredKeys(bool $block_is_fully_v
   public function testValidation(): void {
     // Create a data definition that specifies certain allowed keys.
     $definition = MapDataDefinition::create('mapping')
-      ->addConstraint('ValidKeys', ['north', 'south', 'west']);
+      ->addConstraint('ValidKeys', ['allowedKeys' => ['north', 'south', 'west']]);
     $definition['mapping'] = [
       'north' => ['type' => 'string', 'requiredKey' => FALSE],
       'east' => ['type' => 'string', 'requiredKey' => FALSE],
@@ -338,7 +338,7 @@ public function testValidKeyInference(): void {
     // Ensure that ValidKeys will freak out if the option is not exactly
     // `<infer>`.
     $config->getDataDefinition()
-      ->addConstraint('ValidKeys', 'infer');
+      ->addConstraint('ValidKeys', ['allowedKeys' => 'infer']);
     $this->expectExceptionMessage("'infer' is not a valid set of allowed keys.");
     $config->validate();
   }
-- 
GitLab


From 694b4533f9be45deee456bf4105b52df41b33849 Mon Sep 17 00:00:00 2001
From: Edward Wu <godotislate@gmail.com>
Date: Sat, 31 May 2025 18:47:48 -0700
Subject: [PATCH 18/18] Fix tests 6.

---
 core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php | 2 +-
 .../src/Plugin/Derivative/ExtraFieldBlockDeriver.php            | 2 +-
 .../layout_builder/src/Plugin/Derivative/FieldBlockDeriver.php  | 2 +-
 .../KernelTests/Core/Entity/BundleConstraintValidatorTest.php   | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php
index 66b0f6871c5d..52e54c44358b 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php
@@ -23,7 +23,7 @@
  * type and bundle of the referenced entity can be created as following:
  * @code
  * $definition = \Drupal\Core\Entity\EntityDefinition::create($entity_type)
- *   ->addConstraint('Bundle', $bundle);
+ *   ->addConstraint('Bundle', ['bundle' => $bundle]);
  * \Drupal\Core\TypedData\DataReferenceDefinition::create('entity')
  *   ->setTargetDefinition($definition);
  * @endcode
diff --git a/core/modules/layout_builder/src/Plugin/Derivative/ExtraFieldBlockDeriver.php b/core/modules/layout_builder/src/Plugin/Derivative/ExtraFieldBlockDeriver.php
index 9a0dd6a9782c..20cbac90a689 100644
--- a/core/modules/layout_builder/src/Plugin/Derivative/ExtraFieldBlockDeriver.php
+++ b/core/modules/layout_builder/src/Plugin/Derivative/ExtraFieldBlockDeriver.php
@@ -132,7 +132,7 @@ public function getDerivativeDefinitions($base_plugin_definition) {
           $derivative['admin_label'] = $extra_field['label'];
 
           $context_definition = EntityContextDefinition::fromEntityType($entity_type)
-            ->addConstraint('Bundle', [$bundle_id]);
+            ->addConstraint('Bundle', ['bundle' => [$bundle_id]]);
           $derivative['context_definitions'] = [
             'entity' => $context_definition,
           ];
diff --git a/core/modules/layout_builder/src/Plugin/Derivative/FieldBlockDeriver.php b/core/modules/layout_builder/src/Plugin/Derivative/FieldBlockDeriver.php
index e01a03002012..a4440c20f7f8 100644
--- a/core/modules/layout_builder/src/Plugin/Derivative/FieldBlockDeriver.php
+++ b/core/modules/layout_builder/src/Plugin/Derivative/FieldBlockDeriver.php
@@ -143,7 +143,7 @@ public function getDerivativeDefinitions($base_plugin_definition) {
           $derivative['_block_ui_hidden'] = !$field_definition->isDisplayConfigurable('view');
 
           $context_definition = EntityContextDefinition::fromEntityTypeId($entity_type_id)->setLabel($entity_type_labels[$entity_type_id]);
-          $context_definition->addConstraint('Bundle', [$bundle]);
+          $context_definition->addConstraint('Bundle', ['bundle' => [$bundle]]);
           $derivative['context_definitions'] = [
             'entity' => $context_definition,
             'view_mode' => new ContextDefinition('string'),
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/BundleConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/BundleConstraintValidatorTest.php
index 406de9bb3413..c1af7242cf84 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/BundleConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/BundleConstraintValidatorTest.php
@@ -56,7 +56,7 @@ public function testValidation(): void {
   protected function assertValidation($bundle): void {
     // Create a typed data definition with a Bundle constraint.
     $definition = DataDefinition::create('entity_reference')
-      ->addConstraint('Bundle', $bundle);
+      ->addConstraint('Bundle', ['bundle' => $bundle]);
 
     // Test the validation.
     $node = $this->container->get('entity_type.manager')->getStorage('node')->create(['type' => 'foo']);
-- 
GitLab