Aller au contenu principal

Relations entre les entités

Comment gérer les relations entre les entités avec Doctrine

Notions théoriques

Dans une base de données relationnelle, les tables peuvent être liées entre elles par des relations.

Doctrine supporte les types de relations suivants :

  • OneToOne,
  • OneToMany,
  • ManyToOne
  • et ManyToMany.
Exemple de relation 'Catégories d'un joueur'

Par exemple, si chaque joueur de notre jeu a plusieurs personnages, nous pourrions avoir une relation OneToMany entre l'entité Player et une nouvelle entité Character.

Pour cela, nous devons :

  • ajouter une propriété characters à Player
  • ajouter une propriété player à Character,
  • utiliser les attributs de Doctrine pour définir la relation.

Exemple de mise en application

Voici une entité Character dans le fichier src/Entity/Character.php :

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use App\Repository\CharacterRepository;

#[ORM\Entity(repositoryClass: CharacterRepository::class)]
#[ORM\Table(name: 'tbl_character')]
class Character
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column(type: "integer")]
private ?int $id = null;

#[ORM\Column(type: "string", length: 100)]
private string $name;

#[ORM\ManyToOne(targetEntity: Player::class, inversedBy: "characters")]
private Player $player;

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

public function getName(): string
{
return $this->name;
}

public function setName(string $name): self
{
$this->name = $name;
return $this;
}

public function getPlayer(): Player
{
return $this->player;
}

public function setPlayer(Player $player): self
{
$this->player = $player;
return $this;
}
}
Exemple de relation 'Personnages d'un joueur'

Et voici l'ajout de la propriété characters dans l'entité Player :

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\PlayerRepository;

#[ORM\Entity(repositoryClass: PlayerRepository::class)]
#[ORM\Table(name: 'tbl_player')]
class Player
{
// ... autres propriétés ...

#[ORM\OneToMany(targetEntity: Character::class, mappedBy: "player")]
private Collection $characters;

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

public function getCharacters(): Collection
{
return $this->characters;
}

public function addCharacter(Character $character): self
{
if (!$this->characters->contains($character)) {
$this->characters[] = $character;
$character->setPlayer($this);
}
return $this;
}

public function removeCharacter(Character $character): self
{
if ($this->characters->removeElement($character)) {
if ($character->getPlayer() === $this) {
$character->setPlayer(null);
}
}
return $this;
}
}

Test de mémorisation/compréhension


Quels types de relations Doctrine supporte-t-il ?


Comment définissons-nous une relation ManyToOne dans Symfony ?


Comment définissons-nous une relation OneToMany dans Symfony ?


Que doit-on initialiser dans le constructeur d'une entité qui a une relation OneToMany ?



TP pour réfléchir et résoudre des problèmes

Votre défi pour aujourd'hui consiste à créer une relation entre les joueurs et les personnages

Étape 1 : Créez l'entité Character avec une propriété player en relation ManyToOne vers Player, et ajoutez la relation OneToMany correspondante dans Player.

Étape 2 : Codez l'action createCharacter dans le GameController pour créer un personnage associé à un joueur existant.


Bonne pratique - Définir les deux côtés d'une relation

Dans une relation bidirectionnelle (OneToMany / ManyToOne), il faut initialiser les deux côtés. $character->setPlayer($player) définit le côté ManyToOne (côté base de données). Pour que $player->getCharacters() reste cohérent en mémoire dans la même requête, il faut aussi appeler $player->addCharacter($character). Doctrine ne synchronise les deux côtés que lors du prochain chargement depuis la base de données.

Une solution