Sobes.tech
Middle+

Come implementare la relazione 'has many through' in Doctrine?

sobes.tech AI

Risposta dell'AI

In Doctrine2, la relazione "has many through" viene implementata creando esplicitamente un'entità intermedia (join entity), che collega due altre entità tramite relazioni Many-to-One.

  1. Si creano tre classi di entità: Sorgente, Destinazione e Intermedia (JoinEntity).

  2. L'entità intermedia (ad esempio, UserGroup per "User ha molti Group tramite UserGroup") contiene due campi che sono relazioni Many-to-One con le entità Sorgente (User) e Destinazione (Group).

  3. Le entità Sorgente e Destinazione hanno una relazione One-to-Many con l'entità intermedia.

  4. Per facilitare l'accesso alle entità Destinazione tramite la Sorgente (ad esempio, ottenere tutti i Group per un User), si può aggiungere un metodo nell'entità Sorgente che filtra le entità intermedie.

Esempio di struttura:

// 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; // Relazione con l'entità intermedia

    public function __construct()
    {
        $this->userGroups = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    /**
     * Ottenere i gruppi dell'utente tramite l'entità intermedia.
     * Questo è l'equivalente di "has many through".
     * Nota: Potresti voler recuperare questa collezione in modo efficiente a seconda delle tue esigenze.
     */
    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)) {
            // Imposta il lato proprietario a null (a meno che non sia già stato modificato)
            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; // Relazione con l'entità intermedia

    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)) {
            // Imposta il lato proprietario a null (a meno che non sia già stato modificato)
            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')] // Esempio di nome tabella
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 a User

    #[ORM\ManyToOne(targetEntity: Group::class, inversedBy: 'userGroups')]
    #[ORM\JoinColumn(nullable: false)]
    private ?Group $group = null; // Many-to-One a 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;
    }
}

Questo approccio, anche se richiede la creazione di un'entità aggiuntiva, fornisce un controllo completo sui dati delle relazioni e permette di aggiungere facilmente campi aggiuntivi nella tabella intermedia (ad esempio, la data di adesione dell'utente al gruppo).