title: Symfony2.7 field validates twice, using validation-groups and UniqueEntity
body: <p>Field gets validated twice, causing 2 DB calls and 2 of the same error message.</p>
  
  <p>I have Subscriber class the main User, Registration class and RegistrationType which embeds SubscriberType as tutorial <a href="http://symfony.com/doc/current/cookbook/doctrine/registration_form.html" rel="nofollow">How to Implement a simple Registration Form</a> suggests. The main username field has constraint of UniqueEntity, and any time a non-unique username is entered the "username already exists" error message pops up twice, checking the log DB call to check for existing username is called twice. I've googled this and have not found a same case as mine, but have found similar cases. I have no idea what to do here.</p>
  
  <p>The database is legacy so I couldn't just switch to FOSUserBundle, and the reason I don't use @ annotations but instead use <strong>*.orm.yml</strong> and <strong>validation.yml</strong></p>
  
  <pre class="lang-php prettyprint-override"><code>class RegistrationType extends AbstractType
  {
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
  
          if(!$options['service']){
              $builder-&gt;add('user', new SubscriberType());
              $builder-&gt;add('Register', 'submit');    
          } else {
              $subscriber = new Subscriber();                
              $subscriber-&gt;setEmail($options['user_information']-&gt;getEmail());
              $subscriber-&gt;setFirstname($options['user_information']-&gt;getFirstname());
              $subscriber-&gt;setLastname($options['user_information']-&gt;getLastname());
              $subscriber-&gt;setUsername($options['user_information']-&gt;getEmail());
  
              switch($options['service']){
                  case 'facebook':
                      $subscriber-&gt;setFbEmail($options['user_information']-&gt;getEmail());
                  break;
              }
  
              $builder-&gt;add('user', new SubscriberType($subscriber), array('service' =&gt; $options['service'],
                  'user_information' =&gt; $options['user_information'], 'data'=&gt; $subscriber));
          }
  
          $builder-&gt;add(
              'terms',
              'checkbox',
              array('property_path' =&gt; 'termsAccepted')
          );
  
      }    
  
      public function configureOptions(OptionsResolver $resolver)
      {
          $resolver-&gt;setDefaults(array(
              'service' =&gt; false,
              'user_information' =&gt; false
              ,'validation_groups' =&gt; array('Default', 'registration')
          ));
      }
  
      public function getName()
      {
          return 'registration';
      }
  }
  </code></pre>
  
  <p>the custom <code>'service'</code> options is to integrate HWIOAuthBundle, whole other story there.</p>
  
  <pre class="lang-php prettyprint-override"><code>class SubscriberType extends AbstractType
  {
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
          if(!$options['service']){
              $builder-&gt;add('username', 'email');
              $builder-&gt;add('plainPassword', 'repeated', array(
                 'first_name'  =&gt; 'password',
                 'second_name' =&gt; 'confirm',
                 'type'        =&gt; 'password',
              ));
          } else {
  
  
              $builder-&gt;add('email', 'text', array('read_only' =&gt; true, ));
              $builder-&gt;add('firstname', 'text');
              $builder-&gt;add('lastname', 'text');
              $builder-&gt;add('username', 'hidden');
              $builder-&gt;add('fbEmail', 'hidden');
              $builder-&gt;add('gplusEmail', 'hidden');
          }
      }
  
      public function configureOptions(OptionsResolver $resolver)
      {
          $resolver-&gt;setDefaults(array(
              'data_class' =&gt; 'AppBundle\Entity\Subscriber',
              'service' =&gt; false,
              'user_information' =&gt; false
              ,'validation_groups' =&gt; array('Default', 'registration')
          ));
      }
  
      /* DEPRECATED since 2.7
      public function setDefaultOptions(OptionsResolverInterface $resolver)
      {
          $resolver-&gt;setDefaults(array(
              'data_class' =&gt; 'AppBundle\Entity\Subscriber',
              'service' =&gt; false,
              'user_information' =&gt; false
          ));
      }
      */
  
      public function getName()
      {
          return 'subscriber';
      }
  }
  </code></pre>
  
  <p>Registration.php:</p>
  
  <pre class="lang-php prettyprint-override"><code>&lt;?php
  // src/AppBundle/Form/Model/Registration
  
  namespace AppBundle\Form\Model;
  
  use Symfony\Component\Validator\Constraints as Assert;
  use AppBundle\Entity\Subscriber;
  
  class Registration
  {
      /**
       * @Assert\Type(type="AppBundle\Entity\Subscriber")
       * @Assert\Valid()
       */
      protected $user;
  
      /**
       * @Assert\NotBlank()
       * @Assert\True()
       */
      protected $termsAccepted;
  
      public function setUser(Subscriber $user)
      {
          $this-&gt;user = $user;
      }
  
      public function getUser()
      {
          return $this-&gt;user;
      }
  
      public function getTermsAccepted()
      {
          return $this-&gt;termsAccepted;
      }
  
      public function setTermsAccepted($termsAccepted)
      {
          $this-&gt;termsAccepted = (bool) $termsAccepted;
      }
  }
  </code></pre>
  
  <p>Subscriber.php //Entity:</p>
  
  <pre class="lang-php prettyprint-override"><code>&lt;?php
  
  namespace AppBundle\Entity;
  
  use Doctrine\ORM\Mapping as ORM;
  use Symfony\Component\Security\Core\User\UserInterface;
  use Doctrine\Common\Collections\ArrayCollection;
  //use FOS\UserBundle\Entity\User as BaseUser;
  /**
   * Subscriber
   */
  class Subscriber implements UserInterface, \Serializable
  {
      /**
       * @var integer
       */
      private $id;
  
      /**
       * @var string
       */
      private $username;
  
      /**
       * @var string
       */
      private $email;
  
      /**
       * @var string
       */
      private $gplusEmail;
  
      /**
       * @var string
       */
      private $fbEmail;
  
      /**
       * @var string
       */
      private $sha1Password;
  
      /**
       * @var string
       */
      private $salt;
  
      /**
       * @var float
       */
      private $unit;
  
      /**
       * @var string
       */
      private $validate;
  
      /**
       * @var string
       */
      private $rememberKey;
  
      /**
       * @var string
       */
      private $firstname = '';
  
      /**
       * @var string
       */
      private $lastname = '';
  
      /**
       * @var string
       */
      private $avatar = '';
  
      /**
       * @var string
       */
      private $countryId = '';
  
      /**
       * @var string
       */
      private $address;
  
      /**
       * @var string
       */
      private $phone;
  
      /**
       * @var string
       */
      private $gender = '';
  
      /**
       * @var \DateTime
       */
      private $birthdate;
  
      /**
       * @var \DateTime
       */
      private $lastAttemp;
  
      /**
       * @var integer
       */
      private $attempCount;
  
      /**
       * @var \DateTime
       */
      private $lastloginDate;
  
      /**
       * @var \DateTime
       */
      private $createdAt;
  
      /**
       * @var \DateTime
       */
      private $updatedAt;
  
      /**
       * @var \Doctrine\Common\Collections\Collection
       */
      private $groups;
  
  
      /**
       * @ORM\Column(type="string", length=255)
       */
      protected $plainPassword;
  
      /**
       * Constructor
       */
      public function __construct()
      {
          $this-&gt;groups = new \Doctrine\Common\Collections\ArrayCollection();
      }
  
      /**
       * Get id
       *
       * @return integer 
       */
      public function getId()
      {
          return $this-&gt;id;
      }
  
      /**
       * Set username
       *
       * @param string $username
       * @return Subscriber
       */
      public function setUsername($username)
      {
          $this-&gt;username = $username;
  
          return $this;
      }
  
      /**
       * Get username
       *
       * @return string 
       */
      public function getUsername()
      {
          return $this-&gt;username;
      }
  
      /**
       * Set email
       *
       * @param string $email
       * @return Subscriber
       */
      public function setEmail($email)
      {
          $this-&gt;email = $email;
  
          return $this;
      }
  
      /**
       * Get email
       *
       * @return string 
       */
      public function getEmail()
      {
          return $this-&gt;email;
      }
  
      /**
       * Set gplusEmail
       *
       * @param string $gplusEmail
       * @return Subscriber
       */
      public function setGplusEmail($gplusEmail)
      {
          $this-&gt;gplusEmail = $gplusEmail;
  
          return $this;
      }
  
      /**
       * Get gplusEmail
       *
       * @return string 
       */
      public function getGplusEmail()
      {
          return $this-&gt;gplusEmail;
      }
  
      /**
       * Set fbEmail
       *
       * @param string $fbEmail
       * @return Subscriber
       */
      public function setFbEmail($fbEmail)
      {
          $this-&gt;fbEmail = $fbEmail;
  
          return $this;
      }
  
      /**
       * Get fbEmail
       *
       * @return string 
       */
      public function getFbEmail()
      {
          return $this-&gt;fbEmail;
      }
  
      /**
       * Set sha1Password
       *
       * @param string $sha1Password
       * @return Subscriber
       */
      public function setSha1Password($sha1Password)
      {
          $this-&gt;sha1Password = $sha1Password;
  
          return $this;
      }
  
      /**
       * Get sha1Password
       *
       * @return string 
       */
      public function getSha1Password()
      {
          return $this-&gt;sha1Password;
      }
  
      /**
       * Set salt
       *
       * @param string $salt
       * @return Subscriber
       */
      public function setSalt($salt)
      {
          $this-&gt;salt = $salt;
  
          return $this;
      }
  
      /**
       * Get salt
       *
       * @return string 
       */
      public function getSalt()
      {
          return $this-&gt;salt;
      }
  
      /**
       * Set unit
       *
       * @param float $unit
       * @return Subscriber
       */
      public function setUnit($unit)
      {
          $this-&gt;unit = $unit;
  
          return $this;
      }
  
      /**
       * Get unit
       *
       * @return float 
       */
      public function getUnit()
      {
          return $this-&gt;unit;
      }
  
      /**
       * Set validate
       *
       * @param string $validate
       * @return Subscriber
       */
      public function setValidate($validate)
      {
          $this-&gt;validate = $validate;
  
          return $this;
      }
  
      /**
       * Get validate
       *
       * @return string 
       */
      public function getValidate()
      {
          return $this-&gt;validate;
      }
  
      /**
       * Set rememberKey
       *
       * @param string $rememberKey
       * @return Subscriber
       */
      public function setRememberKey($rememberKey)
      {
          $this-&gt;rememberKey = $rememberKey;
  
          return $this;
      }
  
      /**
       * Get rememberKey
       *
       * @return string 
       */
      public function getRememberKey()
      {
          return $this-&gt;rememberKey;
      }
  
      /**
       * Set firstname
       *
       * @param string $firstname
       * @return Subscriber
       */
      public function setFirstname($firstname)
      {
          $this-&gt;firstname = $firstname;
  
          return $this;
      }
  
      /**
       * Get firstname
       *
       * @return string 
       */
      public function getFirstname()
      {
          return $this-&gt;firstname;
      }
  
      /**
       * Set lastname
       *
       * @param string $lastname
       * @return Subscriber
       */
      public function setLastname($lastname)
      {
          $this-&gt;lastname = $lastname;
  
          return $this;
      }
  
      /**
       * Get lastname
       *
       * @return string 
       */
      public function getLastname()
      {
          return $this-&gt;lastname;
      }
  
      /**
       * Set avatar
       *
       * @param string $avatar
       * @return Subscriber
       */
      public function setAvatar($avatar)
      {
          $this-&gt;avatar = $avatar;
  
          return $this;
      }
  
      /**
       * Get avatar
       *
       * @return string 
       */
      public function getAvatar()
      {
          return $this-&gt;avatar;
      }
  
      /**
       * Set countryId
       *
       * @param string $countryId
       * @return Subscriber
       */
      public function setCountryId($countryId)
      {
          $this-&gt;countryId = $countryId;
  
          return $this;
      }
  
      /**
       * Get countryId
       *
       * @return string 
       */
      public function getCountryId()
      {
          return $this-&gt;countryId;
      }
  
      /**
       * Set address
       *
       * @param string $address
       * @return Subscriber
       */
      public function setAddress($address)
      {
          $this-&gt;address = $address;
  
          return $this;
      }
  
      /**
       * Get address
       *
       * @return string 
       */
      public function getAddress()
      {
          return $this-&gt;address;
      }
  
      /**
       * Set phone
       *
       * @param string $phone
       * @return Subscriber
       */
      public function setPhone($phone)
      {
          $this-&gt;phone = $phone;
  
          return $this;
      }
  
      /**
       * Get phone
       *
       * @return string 
       */
      public function getPhone()
      {
          return $this-&gt;phone;
      }
  
      /**
       * Set gender
       *
       * @param string $gender
       * @return Subscriber
       */
      public function setGender($gender)
      {
          $this-&gt;gender = $gender;
  
          return $this;
      }
  
      /**
       * Get gender
       *
       * @return string 
       */
      public function getGender()
      {
          return $this-&gt;gender;
      }
  
      /**
       * Set birthdate
       *
       * @param \DateTime $birthdate
       * @return Subscriber
       */
      public function setBirthdate($birthdate)
      {
          $this-&gt;birthdate = $birthdate;
  
          return $this;
      }
  
      /**
       * Get birthdate
       *
       * @return \DateTime 
       */
      public function getBirthdate()
      {
          return $this-&gt;birthdate;
      }
  
      /**
       * Set lastAttemp
       *
       * @param \DateTime $lastAttemp
       * @return Subscriber
       */
      public function setLastAttemp($lastAttemp)
      {
          $this-&gt;lastAttemp = $lastAttemp;
  
          return $this;
      }
  
      /**
       * Get lastAttemp
       *
       * @return \DateTime 
       */
      public function getLastAttemp()
      {
          return $this-&gt;lastAttemp;
      }
  
      /**
       * Set attempCount
       *
       * @param integer $attempCount
       * @return Subscriber
       */
      public function setAttempCount($attempCount)
      {
          $this-&gt;attempCount = $attempCount;
  
          return $this;
      }
  
      /**
       * Get attempCount
       *
       * @return integer 
       */
      public function getAttempCount()
      {
          return $this-&gt;attempCount;
      }
  
      /**
       * Set lastloginDate
       *
       * @param \DateTime $lastloginDate
       * @return Subscriber
       */
      public function setLastloginDate($lastloginDate)
      {
          $this-&gt;lastloginDate = $lastloginDate;
  
          return $this;
      }
  
      /**
       * Get lastloginDate
       *
       * @return \DateTime 
       */
      public function getLastloginDate()
      {
          return $this-&gt;lastloginDate;
      }
  
      /**
       * Set createdAt
       *
       * @param \DateTime $createdAt
       * @return Subscriber
       */
      public function setCreatedAt($createdAt)
      {
          $this-&gt;createdAt = $createdAt;
  
          return $this;
      }
  
      /**
       * Get createdAt
       *
       * @return \DateTime 
       */
      public function getCreatedAt()
      {
          return $this-&gt;createdAt;
      }
  
      /**
       * Set updatedAt
       *
       * @param \DateTime $updatedAt
       * @return Subscriber
       */
      public function setUpdatedAt($updatedAt)
      {
          $this-&gt;updatedAt = $updatedAt;
  
          return $this;
      }
  
      /**
       * Get updatedAt
       *
       * @return \DateTime 
       */
      public function getUpdatedAt()
      {
          return $this-&gt;updatedAt;
      }
  
      /**
       * Add groups
       *
       * @param \AppBundle\Entity\SubscriberGroup $groups
       * @return Subscriber
       */
      public function addGroup(\AppBundle\Entity\SubscriberGroup $groups)
      {
          $this-&gt;groups[] = $groups;
  
          return $this;
      }
  
      /**
       * Remove groups
       *
       * @param \AppBundle\Entity\SubscriberGroup $groups
       */
      public function removeGroup(\AppBundle\Entity\SubscriberGroup $groups)
      {
          $this-&gt;groups-&gt;removeElement($groups);
      }
  
      /**
       * Get groups
       *
       * @return \Doctrine\Common\Collections\Collection 
       */
      public function getGroups()
      {
          return $this-&gt;groups;
      }
  
  
  
      /**
       * Get setPassword
       *
       * @param string 
       */
      public function setPassword($hashed_password)
      {
          return $this-&gt;setSha1Password($hashed_password);
      }
  
      /**
       * Get setPassword
       *
       * @return string 
       */
      public function getPassword()
      {
          return $this-&gt;sha1Password;
      }
  
  
      ///*
      public function serialize()
      {
          return serialize(array(
              $this-&gt;id,
              $this-&gt;username,
              $this-&gt;sha1Password,
              $this-&gt;salt
          ));
      }
  
      ///*
      public function unserialize($serialized)
      {
          list (
              $this-&gt;id,
              $this-&gt;username,
              $this-&gt;sha1Password,
              $this-&gt;salt
          ) = unserialize($serialized);
      }
      //*/
  
      public function getRoles()
      {
          $roles = array();
          //var_dump($this-&gt;getGroups());
          foreach($this-&gt;getGroups() as $group){
              $roles[] = $group-&gt;getRole();
          }
          $roles[] = "ROLE_SUBSCRIBER";
          return array_unique($roles);
      }
  
      public function eraseCredentials()
      {
      }
  
      public function getPlainPassword()
      {
          return $this-&gt;plainPassword;
      }
  
      public function setPlainPassword($password)
      {
          $this-&gt;plainPassword = $password;
      }
  
  }
  </code></pre>
  
  <p>And Subscriber.orm.yml as follows:</p>
  
  <pre class="lang-yaml prettyprint-override"><code>AppBundle\Entity\Subscriber:
      type: entity
      table: w_subscriber
      repositoryClass: AppBundle\Entity\SubscriberRepository
      id:
          id:
              type: bigint
              nullable: false
              unsigned: false
              comment: ''
              id: true
              generator:
                  strategy: IDENTITY
      fields:
          username:
              type: string
              nullable: false
              length: 50
              fixed: false
              comment: ''
          email:
              type: string
              nullable: false
              length: 150
              fixed: false
              comment: ''
          gplusEmail:
              type: string
              nullable: true
              length: 150
              fixed: false
              comment: ''
              column: gplus_email
          fbEmail:
              type: string
              nullable: true
              length: 150
              fixed: false
              comment: ''
              column: fb_email
          sha1Password:
              type: string
              nullable: true
              length: 40
              fixed: false
              comment: ''
              column: sha1_password
          salt:
              type: string
              nullable: true
              length: 32
              fixed: false
              comment: ''
          unit:
              type: float
              nullable: true
              precision: 18
              scale: 2
              comment: ''
              default: '0.00'
          validate:
              type: string
              nullable: true
              length: 10
              fixed: false
              comment: ''
          rememberKey:
              type: string
              nullable: true
              length: 50
              fixed: false
              comment: ''
              column: remember_key
          firstname:
              type: string
              nullable: false
              length: 200
              fixed: false
              comment: ''
          lastname:
              type: string
              nullable: true
              length: 200
              fixed: false
              comment: ''
          avatar:
              type: string
              nullable: false
              length: 200
              fixed: false
              comment: ''
          countryId:
              type: string
              nullable: false
              length: 5
              fixed: false
              comment: ''
              default: MN
              column: country_id
          address:
              type: text
              nullable: true
              length: null
              fixed: false
              comment: ''
          phone:
              type: string
              nullable: true
              length: 50
              fixed: false
              comment: ''
          gender:
              type: string
              nullable: false
              length: 1
              fixed: false
              comment: ''
              default: M
          birthdate:
              type: date
              nullable: true
              comment: ''
          lastAttemp:
              type: datetime
              nullable: true
              comment: ''
              column: last_attemp
          attempCount:
              type: bigint
              nullable: true
              unsigned: false
              comment: ''
              default: '0'
              column: attemp_count
          lastloginDate:
              type: datetime
              nullable: true
              comment: ''
              column: lastlogin_date
          createdAt:
              type: datetime
              nullable: false
              comment: ''
              column: created_at
              gedmo:
                  timestampable:
                    on: create
          updatedAt:
              type: datetime
              nullable: false
              comment: ''
              column: updated_at
              gedmo:
                  timestampable:
                      on: update
      lifecycleCallbacks: {  }
      manyToMany:
          groups:
            targetEntity: SubscriberGroup
            joinTable:
              name: w_subscriber_credential
              joinColumns:
                subscriber_id:
                  referencedColumnName: id
              inverseJoinColumns:
                group_id:
                  referencedColumnName: id
  </code></pre>
  
  <p>and finally validation.yml</p>
  
  <pre class="lang-yaml prettyprint-override"><code># src/AppBundle/Resources/config/validation.yml
  AppBundle\Entity\Subscriber:
      constraints:
        - \Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity:
            fields: [ username ]
            groups: [ registration ]
            message: 'this email is already registered'
      properties:
          email:
              - Email: ~
  </code></pre>
  
  <p>the controller:</p>
  
  <pre class="lang-php prettyprint-override"><code>&lt;?php
  // src/AppBundle/Controller/SecurityController.php
  
  namespace AppBundle\Controller;
  
  use Symfony\Component\HttpFoundation\Request;
  use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
  use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  
  use AppBundle\Form\Type\RegistrationType;
  use AppBundle\Form\Model\Registration;
  
  class SecurityController extends Controller
  {
  
      /**
       * @Route("/register", name="register")
       */
      public function registerAction()
      {
          $form = $this-&gt;createForm(new RegistrationType(), new Registration(), array(
              'action' =&gt; $this-&gt;generateUrl('register_create'),
          ));
  
          return $this-&gt;render(
              'security/register.html.twig',
              array('form' =&gt; $form-&gt;createView())
          );
      }
  
      /**
       * @Route("/register/create", name="register_create")
       */    
      public function createAction(Request $request)
      {
          $em = $this-&gt;getDoctrine()-&gt;getManager();
  
          $form = $this-&gt;createForm(new RegistrationType(), new Registration());
  
          $form-&gt;handleRequest($request);
  
          if ($form-&gt;isValid()) {
              $registration = $form-&gt;getData();
              $user = $registration-&gt;getUser();
  
              $user-&gt;setEmail($user-&gt;getUsername());
              $this-&gt;get('my.oauth_aware.user_provider.service')-&gt;setUserPassword($user, $user-&gt;getPlainPassword());
  
              $em-&gt;persist($registration-&gt;getUser());
              $em-&gt;flush();
  
              //return $this-&gt;redirectToRoute('homepage');
          }
  
          return $this-&gt;render(
              'security/register.html.twig',
              array('form' =&gt; $form-&gt;createView())
          );
      }
  
      /**
       * @Route("/login_check", name="login_check")
       */
      public function loginCheckAction()
      {
          // this controller will not be executed,
          // as the route is handled by the Security system
      }
  }
  </code></pre>
  
  <p>note that username is also the email field, I have not posted the template because it is also rather large, and I believe it has nothing to do with validation, but if anyone wants to see it I would gladly post it.</p>
  
  <p>also among the configurations there is
  <code>config.yml-&gt;framework-&gt;validation-&gt;enable_annotations-&gt;true</code></p>