I have an User entity. And those users can be friends together. So I defined a self referencing manyToMany unidirectional association (because there is always reciprocity is friendship, right ?).
a piece of my user entity in YML
manyToMany:
friendList:
targetEntity: User
joinTable:
name: user_friend
joinColumns:
user_id:
referencedColumnName: id
inverseJoinColumns:
friend_id:
referencedColumnName: id
cascade: [persist]
When I call $user->addFriendList($friend)
, and after a persist and a flush, I have PDOException
:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1-2' for fey 'PRIMARY'
When I check in the logs, I can see that doctrine is trying to exectue the same insert query twice.
For your information, my addFriendList
function
public function addFriendList(User $friend)
{
if (!$this->friendList->contains($friend)) {
$this->friendList[] = $friend;
$friend->addFriendList($this);
}
}
Where am I wrong here ?
I finally found a solution to my issue. However I still don't know if it's a Doctrine2 defect or if it works as designed.
I need to persist my user and flush before adding friends.
So my working code is:
$em->persist($user);
$em-flush();
$user->addFriendList($friend);
$em->persist($user);
$em->flush();
@Reuven, you wrote :
$this->friendList[] = $friend;
$friend->addFriendList($this);
Well, you are inserting the relationship twice.
This should be enough :
$this->friendList[] = $friend;
It's because you didn't specify an mappedBy (owning side) and inversedBy.
Check out this many-to-many relationship between Users and Roles:
/**
* @ORM\Entity
* @ORM\Table(name="user")
*/
class User
{
/**
* @ORM\ManyToMany(targetEntity="Role", mappedBy="users")
* @ORM\JoinTable(name="user_role",
* joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")}
* )
*/
protected $roles;
/**
* Constructor
*/
public function __construct()
{
$this->roles = new ArrayCollection();
}
/**
* Has role
*
* @param Role $role
* @return bool
*/
public function hasRole(Role $role)
{
return $this->roles->contains($role);
}
/**
* Add role
*
* @param Role $role
*/
public function addRole(Role $role)
{
if (!$this->hasRole($role)) {
$this->roles->add($role);
$role->addUser($this);
}
}
/**
* Remove roles
*
* @param Role $role
*/
public function removeRole(Role $role)
{
if ($this->hasRole($role)) {
$this->roles->removeElement($role);
$role->removeUser($this);
}
}
}
.
/**
* @ORM\Entity
* @ORM\Table(name="role")
*/
class Role implements RoleInterface
{
/**
* @ORM\ManyToMany(targetEntity="User", inversedBy="roles")
* @ORM\JoinTable(name="user_role",
* joinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")}
* )
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* Has user
*
* @param User $user
* @return bool
*/
public function hasUser(User $user)
{
return $this->users->contains($user);
}
/**
* Add user
*
* @param User $user
*/
public function addUser(User $user)
{
if (!$this->hasUser($user)) {
$this->users->add($user);
$user->addRole($this);
}
}
/**
* Remove user
*
* @param User $user
*/
public function removeUser(User $user)
{
if ($this->hasUser($user)) {
$this->users->removeElement($user);
$user->addRole($this);
}
}
/**
* Get users
*
* @return ArrayCollection
*/
public function getUsers()
{
return $this->users;
}
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