From bcceaa2e9dfca2777aebaba1059a707d38117bc4 Mon Sep 17 00:00:00 2001 From: Stephan Six Date: Tue, 29 Jul 2025 13:09:35 +0200 Subject: [PATCH 1/2] [WCM] [Validator] Added documentation for the new `comparator` option --- reference/constraints/UniqueEntity.rst | 140 +++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/reference/constraints/UniqueEntity.rst b/reference/constraints/UniqueEntity.rst index 0ab2c0a8cbd..9e9bb27119d 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 Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; + use Symfony\Component\Uid\Uuid; + use Symfony\Component\Validator\Constraints\NotBlank; + use App\Entity\BlogPostTranslation; + + #[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 + + + + + + + + + + + + + + + + + .. code-block:: php + + // src/Form/Data/BlogPostTranslationFormData.php + namespace App\Form\Data; + + use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; + use Symfony\Component\Uid\Uuid; + use Symfony\Component\Validator\Constraints\NotBlank; + use App\Entity\BlogPostTranslation; + + class BlogPostTranslationFormData + { + public function __construct( + public readonly Uuid $postId, + public readonly string $locale, + + #[NotBlank] + public ?string $slug = null, + ) {} + + public static function loadValidatorMetadata(ClassMetadata $metadata): void + { + $metadata->addConstraint(UniqueEntity( + fields: ['locale', 'slug'], + message: 'This slug is already used by another post in this language.', + entityClass: BlogPostTranslation::class, + comparator: [self::class, 'compare'], + errorPath: 'slug' + )); + } + + public static function compare(self $subject, BlogPostTranslation $foundEntity): bool + { + return $foundEntity->getPost()->getId()->equals($this->postId) + && $foundEntity->getLocale() === $this->locale; + } + } + + +.. warning:: + + This option cannot be used in conjunction with the `identifierFieldNames`_ + option. + ``message`` ~~~~~~~~~~~ From 78f8a3bd6592132465496805614f1d3bf87f9cf5 Mon Sep 17 00:00:00 2001 From: Stephan Six Date: Tue, 29 Jul 2025 15:41:29 +0200 Subject: [PATCH 2/2] [WCM] [Validator] Fixed use statements order --- reference/constraints/UniqueEntity.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/constraints/UniqueEntity.rst b/reference/constraints/UniqueEntity.rst index 9e9bb27119d..fd496c09889 100644 --- a/reference/constraints/UniqueEntity.rst +++ b/reference/constraints/UniqueEntity.rst @@ -391,10 +391,10 @@ will be used instead of the default strategy. // 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; - use App\Entity\BlogPostTranslation; #[UniqueEntity( fields: ['locale', 'slug'], @@ -462,10 +462,10 @@ will be used instead of the default strategy. // 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; - use App\Entity\BlogPostTranslation; class BlogPostTranslationFormData { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy