diff --git a/reference/constraints/UniqueEntity.rst b/reference/constraints/UniqueEntity.rst
index 0ab2c0a8cbd..fd496c09889 100644
--- a/reference/constraints/UniqueEntity.rst
+++ b/reference/constraints/UniqueEntity.rst
@@ -361,6 +361,146 @@ this option to specify one or more fields to only ignore ``null`` values on them
database, you might see insertion errors when your application attempts to
persist entities that the ``UniqueEntity`` constraint considers valid.
+``comparator``
+~~~~~~~~~~~~~~
+
+**type**: ``callable`` **default**: ``null``
+
+.. versionadded:: 7.4
+
+ The ``comparator`` option was introduced in Symfony 7.4.
+
+The default strategy to check for equality of the found entity (if any) and
+the current validation subject, when using the `entityClass`_ option in
+combination with the `identifierFieldNames`_ option, is to compare each
+mapping in `identifierFieldNames`_ using strict equality (``===``). Optionally
+after **casting** each to string, if they implement ``\Stringable``.
+
+This works in most cases, but fails if either side of the mapping entry is
+not ``\Stringable`` and doesn't refer to the same value or object. This can
+happen if, for example, the referred-to value in the found entity is a value
+object or an association mapped as part of the entity identity.
+
+To still be able to check for equality, you can supply a ``callable`` which
+will be used instead of the default strategy.
+
+.. configuration-block::
+
+ .. code-block:: php-attributes
+
+ // src/Form/Data/BlogPostTranslationFormData.php
+ namespace App\Form\Data;
+
+ use App\Entity\BlogPostTranslation;
+ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
+ use Symfony\Component\Uid\Uuid;
+ use Symfony\Component\Validator\Constraints\NotBlank;
+
+ #[UniqueEntity(
+ fields: ['locale', 'slug'],
+ message: 'This slug is already used by another post in this language.',
+ entityClass: BlogPostTranslation::class,
+ comparator: [BlogPostTranslationFormData::class, 'compare'],
+ errorPath: 'slug'
+ )]
+ class BlogPostTranslationFormData
+ {
+ public function __construct(
+ public readonly Uuid $postId,
+ public readonly string $locale,
+
+ #[NotBlank]
+ public ?string $slug = null,
+ ) {}
+
+ public static function compare(self $subject, BlogPostTranslation $foundEntity): bool
+ {
+ return $foundEntity->getPost()->getId()->equals($this->postId)
+ && $foundEntity->getLocale() === $this->locale;
+ }
+ }
+
+ .. code-block:: yaml
+
+ # config/validator/validation.yaml
+ App\Form\Data\BlogPostTranslationFormData:
+ constraints:
+ - Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity:
+ fields: [locale, slug]
+ message: 'This slug is already used by another post in this language.'
+ entityClass: App\Entity\BlogPostTranslation,
+ comparator: [App\Form\Data\BlogPostTranslationFormData, compare]
+ errorPath: slug
+
+ .. code-block:: xml
+
+
+
+
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: