I need to validate an email passed by user:
private function validate($value): bool
{
    $violations = $this->validator->validate($value, [
        new Assert\NotBlank(),
        new Assert\Email(),
        new UniqueEntity([
            'entityClass' => User::class,
            'fields' => 'email',
        ])
    ]);
    return count($violations) === 0;
}
But UniqueEntity constraint throws an exception:
Warning:
get_class()expects parameter 1 to be object, string given
Seems like ValidatorInterface::validate() method's first argument awaiting for Entity object with getEmail() method, but it looks ugly. 
Is there any elegant way to validate uniqueness of field passing only scalar value to ValidatorInterface::validate() method?
Seems like there is no built-in Symfony solution to do what I want, so I created custom constraint as Jakub Matczak suggested.
UPD: This solution throws a validation error when you're sending form to edit your entity. To avoid this behavior you'll need to improve this constraint manually.
Constraint:
namespace AppBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
class UniqueValueInEntity extends Constraint
{
    public $message = 'This value is already used.';
    public $entityClass;
    public $field;
    public function getRequiredOptions()
    {
        return ['entityClass', 'field'];
    }
    public function getTargets()
    {
        return self::PROPERTY_CONSTRAINT;
    }
    public function validatedBy()
    {
        return get_class($this).'Validator';
    }
} 
Validator:
namespace AppBundle\Validator\Constraints;
use Doctrine\ORM\EntityManager;
use InvalidArgumentException;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class UniqueValueInEntityValidator extends ConstraintValidator
{
    /**
     * @var EntityManager
     */
    private $em;
    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }
    public function validate($value, Constraint $constraint)
    {
        $entityRepository = $this->em->getRepository($constraint->entityClass);
        if (!is_scalar($constraint->field)) {
            throw new InvalidArgumentException('"field" parameter should be any scalar type');
        }
        $searchResults = $entityRepository->findBy([
            $constraint->field => $value
        ]);
        if (count($searchResults) > 0) {
            $this->context->buildViolation($constraint->message)
                ->addViolation();
        }
    }
}
Service:
services:
    app.validator.unique_value_in_entity:
        class: AppBundle\Validator\Constraints\UniqueValueInEntityValidator
        arguments: ['@doctrine.orm.entity_manager']
        tags:
            - { name: validator.constraint_validator }
Usage example:
private function validate($value): bool
{
    $violations = $this->validator->validate($value, [
        new Assert\NotBlank(),
        new Assert\Email(),
        new UniqueValueInEntity([
            'entityClass' => User::class,
            'field' => 'email',
        ])
    ]);
    return count($violations) === 0;
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With