L.S L.S - 2 months ago 7
MySQL Question

doctrine many-to-many association prePersist data duplication

I'm having the following entity which is used to store users in database, for the moment it's still WIP but i'm having problems with duplication when creating a new user. Each time i add a new user user using the register form and prePersist it duplicates the value from Roles table.
In the addUserRoles i tried something to prevent duplicate entries but without success.
How can i prevent that duplication entry and use the existing one ?



namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;

/**
* User
*
* @ORM\HasLifecycleCallbacks
* @ORM\Table(name="user")
* @ORM\Entity(repositoryClass="AppBundle\Repository\BaseUserRepository")
* @UniqueEntity(fields="email", message="Email already taken")
* @UniqueEntity(fields="username", message="Username already taken")
*/
class BaseUser
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

/**
* @var string
*
* @ORM\Column(name="username", type="string", length=255, unique=true)
* @Assert\NotBlank()
*/
private $username;

/**
* @var string
*
* @ORM\Column(name="email", type="string", length=255, unique=true)
* @Assert\NotBlank()
* @Assert\Email()
*/
private $email;

/**
* @var string
*
* The below length depends on the "algorithm" you use for encoding
* the password, but this works well with bcrypt.
*
* @ORM\Column(name="password", type="string", length=64)
*/
private $password;

/**
* An API token that can be used for this user
*
* @ORM\Column(type="string")
*/
private $token;

/**
* @var int
*
* @ORM\Column(name="isAccountNonExpired", type="boolean")
*/
private $isAccountNonExpired = 1;

/**
* @var int
*
* @ORM\Column(name="isAccountNonLocked", type="boolean")
*/
private $isAccountNonLocked = 1;

/**
* @var int
*
* @ORM\Column(name="isCredentialsNonExpired", type="boolean")
*/
private $isCredentialsNonExpired = 1;

/**
* @var int
*
* @ORM\Column(name="isEnabled", type="boolean")
*/
private $isEnabled = 1;


/**
* @var datetime
*
* @Gedmo\Timestampable(on="create")
* @ORM\Column(name="createdAt", type="datetime")
*/
private $createdAt;

/**
* @var datetime
*
* @Gedmo\Timestampable(on="update")
* @ORM\Column(name="updatedAt", type="datetime")
*/
private $updatedAt;
/**
* @var string
*
* @ORM\ManyToMany(targetEntity="AppBundle\Entity\UserRole", inversedBy="user", cascade={"persist"})
*
*/
private $userRoles;


/**
* @ORM\PrePersist()
*/
public function prePersist()
{
$userRole = new UserRole();
$userRole->setName('ROLE_USER');
$this->addUserRole($userRole);
}

public function __toString()
{
// TODO: Implement __toString() method.
return $this->username;
}


/**
* Constructor
*/
public function __construct()
{
$this->userRoles = new \Doctrine\Common\Collections\ArrayCollection();
}

/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}

/**
* Set username
*
* @param string $username
*
* @return BaseUser
*/
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 BaseUser
*/
public function setEmail($email)
{
$this->email = $email;

return $this;
}

/**
* Get email
*
* @return string
*/
public function getEmail()
{
return $this->email;
}

/**
* Set password
*
* @param string $password
*
* @return BaseUser
*/
public function setPassword($password)
{
$this->password = $password;

return $this;
}

/**
* Get password
*
* @return string
*/
public function getPassword()
{
return $this->password;
}

/**
* Set token
*
* @param string $token
*
* @return BaseUser
*/
public function setToken($token)
{
$this->token = $token;

return $this;
}

/**
* Get token
*
* @return string
*/
public function getToken()
{
return $this->token;
}

/**
* Set isAccountNonExpired
*
* @param integer $isAccountNonExpired
*
* @return BaseUser
*/
public function setIsAccountNonExpired($isAccountNonExpired)
{
$this->isAccountNonExpired = $isAccountNonExpired;

return $this;
}

/**
* Get isAccountNonExpired
*
* @return integer
*/
public function getIsAccountNonExpired()
{
return $this->isAccountNonExpired;
}

/**
* Set isAccountNonLocked
*
* @param integer $isAccountNonLocked
*
* @return BaseUser
*/
public function setIsAccountNonLocked($isAccountNonLocked)
{
$this->isAccountNonLocked = $isAccountNonLocked;

return $this;
}

/**
* Get isAccountNonLocked
*
* @return integer
*/
public function getIsAccountNonLocked()
{
return $this->isAccountNonLocked;
}

/**
* Set isCredentialsNonExpired
*
* @param integer $isCredentialsNonExpired
*
* @return BaseUser
*/
public function setIsCredentialsNonExpired($isCredentialsNonExpired)
{
$this->isCredentialsNonExpired = $isCredentialsNonExpired;

return $this;
}

/**
* Get isCredentialsNonExpired
*
* @return integer
*/
public function getIsCredentialsNonExpired()
{
return $this->isCredentialsNonExpired;
}

/**
* Set isEnabled
*
* @param integer $isEnabled
*
* @return BaseUser
*/
public function setIsEnabled($isEnabled)
{
$this->isEnabled = $isEnabled;

return $this;
}

/**
* Get isEnabled
*
* @return integer
*/
public function getIsEnabled()
{
return $this->isEnabled;
}

/**
* Set createdAt
*
* @param \DateTime $createdAt
*
* @return BaseUser
*/
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 BaseUser
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;

return $this;
}

/**
* Get updatedAt
*
* @return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}

/**
* Add userRole
*
* @param \AppBundle\Entity\UserRole $userRole
*
* @return BaseUser
*/
public function addUserRole(\AppBundle\Entity\UserRole $userRole)
{
/**
* $this->userRoles[] = $userRole;
* return $this;
*/

if(!$this->hasRole($userRole)) {

$this->userRoles->add($userRole);
}

}

public function hasRole(UserRole $role) {

return $this->userRoles->contains($role);

}

/**
* Remove userRole
*
* @param \AppBundle\Entity\UserRole $userRole
*/
public function removeUserRole(\AppBundle\Entity\UserRole $userRole)
{
$this->userRoles->removeElement($userRole);
}

/**
* Get userRoles
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getUserRoles()
{
return $this->userRoles;
}
}

Answer

When you're making this check if(!$this->hasRole($userRole)) you are trying to achieve that one role will not be added twice.

But each time in prePersist you're creating a new Role with the same name. So $this->hasRole($userRole) will return false, as it is completely new object (even so with same role name) and it is not yet present in $this->userRoles arrayCollection.

So you need to find an existing role with a name ROLE_USER and assign it to user. You can make it in controller or with a Doctrine listener service