Create a gist now

Instantly share code, notes, and snippets.

@Ocramius /User.php
Last active Sep 21, 2016

Doctrine 2 ManyToMany - the correct way
<?php
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @ORM\Entity()
* @ORM\Table(name="user")
*/
class User
{
/**
* @var int|null
* @ORM\Id()
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer", name="id")
*/
protected $id;
/**
* @var \Doctrine\Common\Collections\Collection|UserGroup[]
*
* @ORM\ManyToMany(targetEntity="UserGroup", inversedBy="users")
* @ORM\JoinTable(
* name="user_usergroup",
* joinColumns={
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* @ORM\JoinColumn(name="usergroup_id", referencedColumnName="id")
* }
* )
*/
protected $userGroups;
/**
* Default constructor, initializes collections
*/
public function __construct()
{
$this->userGroups = new ArrayCollection();
}
/**
* @param UserGroup $userGroup
*/
public function addUserGroup(UserGroup $userGroup)
{
if ($this->userGroups->contains($userGroup)) {
return;
}
$this->userGroups->add($userGroup);
$userGroup->addUser($this);
}
/**
* @param UserGroup $userGroup
*/
public function removeUserGroup(UserGroup $userGroup)
{
if (!$this->userGroups->contains($userGroup)) {
return;
}
$this->userGroups->removeElement($userGroup);
$userGroup->removeUser($this);
}
}
<?php
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @ORM\Entity()
* @ORM\Table(name="usergroup")
*/
class UserGroup
{
/**
* @var int|null
* @ORM\Id()
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer", name="id")
*/
protected $id;
/**
* @var \Doctrine\Common\Collections\Collection|User[]
*
* @ORM\ManyToMany(targetEntity="User", mappedBy="userGroups")
*/
protected $users;
/**
* Default constructor, initializes collections
*/
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* @param User $user
*/
public function addUser(User $user)
{
if ($this->users->contains($user)) {
return;
}
$this->users->add($user);
$user->addUserGroup($this);
}
/**
* @param User $user
*/
public function removeUser(User $user)
{
if (!$this->users->contains($user)) {
return;
}
$this->users->removeElement($user);
$user->removeUserGroup($this);
}
}
@basz

mappedBy="blocks"??? shouldn't that be mappedBy="groups"

@basz

idem for inversedBy="blocks"?

@Ocramius
Owner

@basz fixed

@JulienDotDev

In User.php :

"protected $groups ;"

but you are using the var $userGroups everywher :

"$this->userGroups = new ArrayCollection();"
...

@Ocramius
Owner
@sliman1345

@Ocramius
No, this not Fixed, this is infinite loop !!

 "addUser"  => "addUserGroup" => "addUser" => "addUserGroup" => "addUser" => ...

Just remove "$user->addUserGroup($this);" from the Entity "UserGroup", its fine.
And keep "$userGroup->addUser($this);" in "User" Entity (synchronously updating inverse side).

@Ocramius
Owner

@sliman1345 I don't see an infinite loop here: the loop is terminated because of early returns in case no operation has to be applied

@fyrye

UserGroups.php

@ORM\ManyToMany(targetEntity="User", mappedBy="groups")

There is no property named $groups, shouldn't it be

@ORM\ManyToMany(targetEntity="User", mappedBy="userGroups")
@Ocramius
Owner

@fyrye thanks, updated!

@Nijusan

so i implemented products and categories with many to many the same way as described, but something i am missing...
when i want all categories of a product ($product-getCategories()) i only get a persistant-collection that is not initialized. so to really get an array of all categories i need to initialize it... ? isnt there a way that this happens automatically as it is with other relations?

on stackoverflow i read something about fetch=EAGER, but then the query for collecting the categories is always fired, even when i am not calling ->getCategories()

@rogerwalt

You could always use the orm:generate-entities command, see here. If your associations are correct, this will produce the correct constructors and getter/setter methods for your entities. Also, the orm:validate-schema command will tell you what is wrong with your entity relations.

@Ocramius
Owner

Please don't use orm:generate-entities: basically means that there is no business logic in your entities.

The example is here to demonstrate how the associations should always be balanced from both sides

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment