Sobes.tech
Middle+

Πώς υλοποιείται η σχέση 'has many through' στο Doctrine;

sobes.tech AI

Απάντηση από AI

Στο Doctrine2, η σχέση "has many through" υλοποιείται μέσω της ρητής δημιουργίας μιας ενδιάμεσης οντότητας (join entity), η οποία συνδέει δύο άλλες οντότητες με σχέσεις Many-to-One.

  1. Δημιουργούνται τρεις κλάσεις οντοτήτων: Πηγή, Στόχος και Ενδιάμεση (JoinEntity).

  2. Η ενδιάμεση οντότητα (π.χ., UserGroup για το "User έχει πολλές Groups μέσω UserGroup") περιέχει δύο πεδία που είναι σχέσεις Many-to-One με τις οντότητες Πηγή (User) και Στόχο (Group).

  3. Οι οντότητες Πηγή και Στόχος έχουν σχέση One-to-Many με την ενδιάμεση οντότητα.

  4. Για εύκολη πρόσβαση στις οντότητες Στόχο μέσω της Πηγής (π.χ., όλες οι Group για έναν User), μπορεί να προστεθεί μια μέθοδος στην οντότητα Πηγή που φιλτράρει τις ενδιάμεσες οντότητες.

Παραδείγματα δομής:

// 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; // Σχέση με την ενδιάμεση οντότητα

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

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

    /**
     * Λήψη ομάδων του χρήστη μέσω της ενδιάμεσης οντότητας.
     * Αυτό είναι το ισοδύναμο του "has many through".
     * Σημείωση: Μπορεί να θέλετε να φορτώσετε αυτή τη συλλογή αποτελεσματικά ανάλογα με τις ανάγκες σας.
     */
    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)) {
            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; // Σχέση με την ενδιάμεση οντότητα

    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)) {
            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')] // Παράδειγμα ονόματος πίνακα
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;
    }
}

Αυτή η προσέγγιση, αν και απαιτεί τη δημιουργία επιπλέον οντότητας, παρέχει πλήρη έλεγχο στις σχέσεις και επιτρέπει εύκολα την προσθήκη επιπλέον πεδίων στον ενδιάμεσο πίνακα (π.χ., ημερομηνία ένταξης χρήστη σε ομάδα).