Field gets validated twice, causing 2 DB calls and 2 of the same error message.
I have Subscriber class the main User, Registration class and RegistrationType which embeds SubscriberType as tutorial How to Implement a simple Registration Form 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.
The database is legacy so I couldn't just switch to FOSUserBundle, and the reason I don't use @ annotations but instead use *.orm.yml and validation.yml
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
if(!$options['service']){
$builder->add('user', new SubscriberType());
$builder->add('Register', 'submit');
} else {
$subscriber = new Subscriber();
$subscriber->setEmail($options['user_information']->getEmail());
$subscriber->setFirstname($options['user_information']->getFirstname());
$subscriber->setLastname($options['user_information']->getLastname());
$subscriber->setUsername($options['user_information']->getEmail());
switch($options['service']){
case 'facebook':
$subscriber->setFbEmail($options['user_information']->getEmail());
break;
}
$builder->add('user', new SubscriberType($subscriber), array('service' => $options['service'],
'user_information' => $options['user_information'], 'data'=> $subscriber));
}
$builder->add(
'terms',
'checkbox',
array('property_path' => 'termsAccepted')
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'service' => false,
'user_information' => false
,'validation_groups' => array('Default', 'registration')
));
}
public function getName()
{
return 'registration';
}
}
the custom 'service'
options is to integrate HWIOAuthBundle, whole other story there.
class SubscriberType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
if(!$options['service']){
$builder->add('username', 'email');
$builder->add('plainPassword', 'repeated', array(
'first_name' => 'password',
'second_name' => 'confirm',
'type' => 'password',
));
} else {
$builder->add('email', 'text', array('read_only' => true, ));
$builder->add('firstname', 'text');
$builder->add('lastname', 'text');
$builder->add('username', 'hidden');
$builder->add('fbEmail', 'hidden');
$builder->add('gplusEmail', 'hidden');
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Subscriber',
'service' => false,
'user_information' => false
,'validation_groups' => array('Default', 'registration')
));
}
/* DEPRECATED since 2.7
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Subscriber',
'service' => false,
'user_information' => false
));
}
*/
public function getName()
{
return 'subscriber';
}
}
Registration.php:
<?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->user = $user;
}
public function getUser()
{
return $this->user;
}
public function getTermsAccepted()
{
return $this->termsAccepted;
}
public function setTermsAccepted($termsAccepted)
{
$this->termsAccepted = (bool) $termsAccepted;
}
}
Subscriber.php //Entity:
<?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->groups = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* @param string $username
* @return Subscriber
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get username
*
* @return string
*/
public function getUsername()
{
return $this->username;
}
/**
* Set email
*
* @param string $email
* @return Subscriber
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set gplusEmail
*
* @param string $gplusEmail
* @return Subscriber
*/
public function setGplusEmail($gplusEmail)
{
$this->gplusEmail = $gplusEmail;
return $this;
}
/**
* Get gplusEmail
*
* @return string
*/
public function getGplusEmail()
{
return $this->gplusEmail;
}
/**
* Set fbEmail
*
* @param string $fbEmail
* @return Subscriber
*/
public function setFbEmail($fbEmail)
{
$this->fbEmail = $fbEmail;
return $this;
}
/**
* Get fbEmail
*
* @return string
*/
public function getFbEmail()
{
return $this->fbEmail;
}
/**
* Set sha1Password
*
* @param string $sha1Password
* @return Subscriber
*/
public function setSha1Password($sha1Password)
{
$this->sha1Password = $sha1Password;
return $this;
}
/**
* Get sha1Password
*
* @return string
*/
public function getSha1Password()
{
return $this->sha1Password;
}
/**
* Set salt
*
* @param string $salt
* @return Subscriber
*/
public function setSalt($salt)
{
$this->salt = $salt;
return $this;
}
/**
* Get salt
*
* @return string
*/
public function getSalt()
{
return $this->salt;
}
/**
* Set unit
*
* @param float $unit
* @return Subscriber
*/
public function setUnit($unit)
{
$this->unit = $unit;
return $this;
}
/**
* Get unit
*
* @return float
*/
public function getUnit()
{
return $this->unit;
}
/**
* Set validate
*
* @param string $validate
* @return Subscriber
*/
public function setValidate($validate)
{
$this->validate = $validate;
return $this;
}
/**
* Get validate
*
* @return string
*/
public function getValidate()
{
return $this->validate;
}
/**
* Set rememberKey
*
* @param string $rememberKey
* @return Subscriber
*/
public function setRememberKey($rememberKey)
{
$this->rememberKey = $rememberKey;
return $this;
}
/**
* Get rememberKey
*
* @return string
*/
public function getRememberKey()
{
return $this->rememberKey;
}
/**
* Set firstname
*
* @param string $firstname
* @return Subscriber
*/
public function setFirstname($firstname)
{
$this->firstname = $firstname;
return $this;
}
/**
* Get firstname
*
* @return string
*/
public function getFirstname()
{
return $this->firstname;
}
/**
* Set lastname
*
* @param string $lastname
* @return Subscriber
*/
public function setLastname($lastname)
{
$this->lastname = $lastname;
return $this;
}
/**
* Get lastname
*
* @return string
*/
public function getLastname()
{
return $this->lastname;
}
/**
* Set avatar
*
* @param string $avatar
* @return Subscriber
*/
public function setAvatar($avatar)
{
$this->avatar = $avatar;
return $this;
}
/**
* Get avatar
*
* @return string
*/
public function getAvatar()
{
return $this->avatar;
}
/**
* Set countryId
*
* @param string $countryId
* @return Subscriber
*/
public function setCountryId($countryId)
{
$this->countryId = $countryId;
return $this;
}
/**
* Get countryId
*
* @return string
*/
public function getCountryId()
{
return $this->countryId;
}
/**
* Set address
*
* @param string $address
* @return Subscriber
*/
public function setAddress($address)
{
$this->address = $address;
return $this;
}
/**
* Get address
*
* @return string
*/
public function getAddress()
{
return $this->address;
}
/**
* Set phone
*
* @param string $phone
* @return Subscriber
*/
public function setPhone($phone)
{
$this->phone = $phone;
return $this;
}
/**
* Get phone
*
* @return string
*/
public function getPhone()
{
return $this->phone;
}
/**
* Set gender
*
* @param string $gender
* @return Subscriber
*/
public function setGender($gender)
{
$this->gender = $gender;
return $this;
}
/**
* Get gender
*
* @return string
*/
public function getGender()
{
return $this->gender;
}
/**
* Set birthdate
*
* @param \DateTime $birthdate
* @return Subscriber
*/
public function setBirthdate($birthdate)
{
$this->birthdate = $birthdate;
return $this;
}
/**
* Get birthdate
*
* @return \DateTime
*/
public function getBirthdate()
{
return $this->birthdate;
}
/**
* Set lastAttemp
*
* @param \DateTime $lastAttemp
* @return Subscriber
*/
public function setLastAttemp($lastAttemp)
{
$this->lastAttemp = $lastAttemp;
return $this;
}
/**
* Get lastAttemp
*
* @return \DateTime
*/
public function getLastAttemp()
{
return $this->lastAttemp;
}
/**
* Set attempCount
*
* @param integer $attempCount
* @return Subscriber
*/
public function setAttempCount($attempCount)
{
$this->attempCount = $attempCount;
return $this;
}
/**
* Get attempCount
*
* @return integer
*/
public function getAttempCount()
{
return $this->attempCount;
}
/**
* Set lastloginDate
*
* @param \DateTime $lastloginDate
* @return Subscriber
*/
public function setLastloginDate($lastloginDate)
{
$this->lastloginDate = $lastloginDate;
return $this;
}
/**
* Get lastloginDate
*
* @return \DateTime
*/
public function getLastloginDate()
{
return $this->lastloginDate;
}
/**
* Set createdAt
*
* @param \DateTime $createdAt
* @return Subscriber
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* @return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set updatedAt
*
* @param \DateTime $updatedAt
* @return Subscriber
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* @return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* Add groups
*
* @param \AppBundle\Entity\SubscriberGroup $groups
* @return Subscriber
*/
public function addGroup(\AppBundle\Entity\SubscriberGroup $groups)
{
$this->groups[] = $groups;
return $this;
}
/**
* Remove groups
*
* @param \AppBundle\Entity\SubscriberGroup $groups
*/
public function removeGroup(\AppBundle\Entity\SubscriberGroup $groups)
{
$this->groups->removeElement($groups);
}
/**
* Get groups
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getGroups()
{
return $this->groups;
}
/**
* Get setPassword
*
* @param string
*/
public function setPassword($hashed_password)
{
return $this->setSha1Password($hashed_password);
}
/**
* Get setPassword
*
* @return string
*/
public function getPassword()
{
return $this->sha1Password;
}
///*
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->sha1Password,
$this->salt
));
}
///*
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->sha1Password,
$this->salt
) = unserialize($serialized);
}
//*/
public function getRoles()
{
$roles = array();
//var_dump($this->getGroups());
foreach($this->getGroups() as $group){
$roles[] = $group->getRole();
}
$roles[] = "ROLE_SUBSCRIBER";
return array_unique($roles);
}
public function eraseCredentials()
{
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
}
}
And Subscriber.orm.yml as follows:
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
and finally validation.yml
# 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: ~
the controller:
<?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->createForm(new RegistrationType(), new Registration(), array(
'action' => $this->generateUrl('register_create'),
));
return $this->render(
'security/register.html.twig',
array('form' => $form->createView())
);
}
/**
* @Route("/register/create", name="register_create")
*/
public function createAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(new RegistrationType(), new Registration());
$form->handleRequest($request);
if ($form->isValid()) {
$registration = $form->getData();
$user = $registration->getUser();
$user->setEmail($user->getUsername());
$this->get('my.oauth_aware.user_provider.service')->setUserPassword($user, $user->getPlainPassword());
$em->persist($registration->getUser());
$em->flush();
//return $this->redirectToRoute('homepage');
}
return $this->render(
'security/register.html.twig',
array('form' => $form->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
}
}
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.
also among the configurations there is
config.yml->framework->validation->enable_annotations->true
I'm sorry for wasting everyone's time. The error was in Declaration of the main AppBundle, or actually having declarations of 2 differnet bundle within 1 bundle file hierarchy.
To override HWIOAuthBundle's templates and controllers, I declared a new bundle within AppBundle, for some reason I thought that a new declaration was required. Silly me.
So symfony loaded validation.yml twice foreach separate declarations of bundles.
By moving getParent function into AppBundle declaration, and removing 2nd Bundle class declaration and its initialization from AppKernel solves the problem.