Sobes.tech
Middle+

Wie implementiert man die Beziehung 'has many through' in Doctrine?

sobes.tech KI

Antwort von AI

In Doctrine2 wird die Beziehung "has many through" durch die explizite Erstellung einer Zwischenentität (Join-Entity) implementiert, die zwei andere Entitäten durch Many-to-One-Beziehungen verbindet.

  1. Es werden drei Entitätsklassen erstellt: Quelle, Ziel und Zwischen (JoinEntity).

  2. Die Zwischenentität (z.B. UserGroup für "User hat viele Group durch UserGroup") enthält zwei Felder, die Many-to-One-Beziehungen zu den Entitäten Quelle (User) und Ziel (Group) sind.

  3. Die Entitäten Quelle und Ziel haben eine One-to-Many-Beziehung zur Zwischenentität.

  4. Um den Zugriff auf die Ziel-Entitäten über die Quelle zu erleichtern (z.B. alle Group für einen User zu erhalten), kann eine Methode in der Quell-Entität hinzugefügt werden, die die Zwischenentitäten filtert.

Beispielstruktur:

// 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; // Beziehung zur Zwischenentität

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

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

    /**
     * Erhalte die Gruppen des Benutzers über die Zwischenentität.
     * Dies ist das Äquivalent zu "has many through".
     * Hinweis: Sie möchten diese Sammlung je nach Bedarf effizient abrufen.
     */
    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)) {
            // Setze die besitzende Seite auf null (sofern noch nicht geändert)
            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; // Beziehung zur Zwischenentität

    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)) {
            // Setze die besitzende Seite auf null (sofern noch nicht geändert)
            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')] // Beispiel für Tabellennamen
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 zu User

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

Dieser Ansatz, obwohl er die Erstellung einer zusätzlichen Entität erfordert, bietet vollständige Kontrolle über die Beziehungsdaten und erleichtert das Hinzufügen zusätzlicher Felder in die Zwischenentität (z.B. Datum, an dem der Benutzer der Gruppe beitritt).