Comment implémenter la relation 'has many through' dans Doctrine?
sobes.tech IA
Réponse de l'IA
Dans Doctrine2, la relation "has many through" est implémentée en créant explicitement une entité intermédiaire (entité de jointure), qui relie deux autres entités par des relations Many-to-One.
-
Trois classes d'entités sont créées : Source, Cible et Intermédiaire (JoinEntity).
-
L'entité intermédiaire (par exemple,
UserGrouppour "User a plusieurs Group via UserGroup") contient deux champs qui sont des relations Many-to-One avec les entités Source (User) et Cible (Group). -
Les entités Source et Cible ont une relation One-to-Many avec l'entité intermédiaire.
-
Pour faciliter l'accès aux entités Cible via l'entité Source (par exemple, obtenir tous les
Grouppour unUser), on peut ajouter une méthode dans l'entité Source qui filtre les entités intermédiaires.
Exemple de structure :
// User.php
<?php
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\OneToMany(targetEntity: UserGroup::class, mappedBy: 'user')]
private Collection $userGroups; // Relation à l'entité intermédiaire
public function __construct()
{
$this->userGroups = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* Obtenir les groupes de l'utilisateur via l'entité intermédiaire.
* Ceci est l'équivalent de "has many through".
* Note : vous pouvez vouloir récupérer cette collection efficacement selon vos besoins.
*/
public function getGroups(): Collection
{
return $this->userGroups->map(fn(UserGroup $userGroup) => $userGroup->getGroup());
}
public function getUserGroups(): Collection
{
return $this->userGroups;
}
public function addUserGroup(UserGroup $userGroup): static
{
if (!$this->userGroups->contains($userGroup)) {
$this->userGroups->add($userGroup);
$userGroup->setUser($this);
}
return $this;
}
public function removeUserGroup(UserGroup $userGroup): static
{
if ($this->userGroups->removeElement($userGroup)) {
// définir le côté propriétaire à null (sauf si déjà changé)
if ($userGroup->getUser() === $this) {
$userGroup->setUser(null);
}
}
return $this;
}
}
// Group.php
<?php
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Group
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\OneToMany(targetEntity: UserGroup::class, mappedBy: 'group')]
private Collection $userGroups; // Relation à l'entité intermédiaire
public function __construct()
{
$this->userGroups = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getUserGroups(): Collection
{
return $this->userGroups;
}
public function addUserGroup(UserGroup $userGroup): static
{
if (!$this->userGroups->contains($userGroup)) {
$this->userGroups->add($userGroup);
$userGroup->setGroup($this);
}
return $this;
}
public function removeUserGroup(UserGroup $userGroup): static
{
if ($this->userGroups->removeElement($userGroup)) {
// définir le côté propriétaire à null (sauf si déjà changé)
if ($userGroup->getGroup() === $this) {
$userGroup->setGroup(null);
}
}
return $this;
}
}
// UserGroup.php
<?php
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\Table(name: 'user_user_group')] // Exemple de nom de table
class UserGroup
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'userGroups')]
#[ORM\JoinColumn(nullable: false)]
private ?User $user = null; // Many-to-One à User
#[ORM\ManyToOne(targetEntity: Group::class, inversedBy: 'userGroups')]
#[ORM\JoinColumn(nullable: false)]
private ?Group $group = null; // Many-to-One à Group
public function getId(): ?int
{
return $this->id;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): static
{
$this->user = $user;
return $this;
}
public function getGroup(): ?Group
{
return $this->group;
}
public function setGroup(?Group $group): static
{
$this->group = $group;
return $this;
}
}
Cette approche, bien qu'exigeant la création d'une entité supplémentaire, offre un contrôle complet sur les données des relations et facilite l'ajout de champs supplémentaires dans la table intermédiaire (par exemple, la date d'adhésion de l'utilisateur au groupe).