Aller au contenu principal

Relations entre les entités

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

Petite vérification​

Avant de commencer, vérifiez si vous savez...

  1. citer l'option qu'on ajoute à un champ FileType pour indiquer qu'il n'est pas lié directement à une propriété de l'entité, et expliquer pourquoi cette option est nécessaire
  2. pour l'entité Character de la séance précédente (formulaire CharacterType avec le champ imageFile non mappé, méthode setImage(?string $image)), prédire ce que contient la propriété image du personnage après l'exécution de :
    $newFilename = uniqid() . '.' . $imageFile->guessExtension();
    $imageFile->move($this->getParameter('characters_directory'), $newFilename);
    $character->setImage($newFilename);
  3. expliquer pourquoi on ne doit jamais utiliser getClientOriginalName() comme nom de destination lors du déplacement d'un fichier envoyé avec move()
📌 Réponse attendue

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