Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Validation with the UniqueEntity Constraint

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

Registration is working, but it's missing validation.

Since the form is bound to the User class, that is where our annotation rules should live. First, you need the use statement for the annotations. We added validation earlier in Genus. So, you can either copy this use statement, grab it from the documentation, or do what I do: cheat by saying use, auto-completing an annotation I know exists - like NotBlank, deleting the last part, and adding the normal as Assert alias:

... lines 1 - 7
use Symfony\Component\Validator\Constraints as Assert;
... lines 9 - 116

We obviously want email to be NotBlank. We also want email to be a valid email address. For plainPassword, that should also not be blank:

... lines 1 - 13
class User implements UserInterface
{
... lines 16 - 22
/**
* @Assert\NotBlank()
* @Assert\Email()
... line 26
*/
private $email;
... lines 29 - 36
/**
... line 38
* @Assert\NotBlank()
... lines 40 - 41
*/
private $plainPassword;
... lines 44 - 114
}

Pretty simple.

Ok, go back, keep the form blank, and submit. Nice validation errors.

Forcing a Unique Email

But check this out: type weaverryan+1@gmail.com. That email is already taken, so I should not be able to do this. But since there aren't any validation rules checking this, the request goes through and the email looks totally valid.

How can we add a validation rule to prevent that? By using a special validation constraint made just for this occasion.

The UniqueEntity Constraint

This constraint's annotation doesn't go above a specific property: it lives above the entire class. Add @UniqueEntity. Notice, this lives in a different namespace than the other annotations, so PhpStorm added its own use statement.

Next configure it. You can always go to the reference section of the docs, or, if you hold command, you can click the annotation to open its class. The public properties are always the options that you can pass to the annotation.

The options we need are fields - which tell it which field needs to be unique in the database - and message so we can say something awesome when it happens.

So add fields={"email"}. This is called fields because you could make this validation be unique across several columns. Then add message="It looks like you already have an account!":

... lines 1 - 4
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
... lines 6 - 10
/**
... lines 12 - 13
* @UniqueEntity(fields={"email"}, message="It looks like your already have an account!")
*/
class User implements UserInterface
... lines 17 - 118

Cool! Go back and hit register again. This just makes me happy!

We're good, right? Well, almost. There's one last gotcha with validation and registration.

Leave a comment!

5
Login or Register to join the conversation
Celestino R. Avatar
Celestino R. Avatar Celestino R. | posted 4 months ago

Hi there, how would work to use the same validation for update? for registration works perfectly but for update if I send the same email complains email already exists.

Reply

Hey Celestino R.

You can use validation groups, or create another FormType only for updates that will change the constraints on your email property
More info about groups here: https://symfony.com/doc/cur...

Cheers!

Reply
Celestino R. Avatar

I don't use forms it's an API. So I created a custom validation as could not see any better way.

Reply

I see, yes, in that case, using validation groups is the way to go

Reply
mehdi Avatar

Hello.
I have 3 entities Business , Person, and Phone. In the Phone Entity I store all phone numbers created from the two other entities. My question is how to validate the Phone entity to be unique inside Person or Business, not unique in the whole table(Phone) ??

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}