08 - Symfony : Relation "Many-To-One" avec Doctrine
Objectifs
- ✅ Mettre en place une relation Many-To-One avec deux entités
Pré-requis
Avoir terminé la quête
Introduction
User stories 📋
en tant que visiteur, je souhaite récupérer les données d'une série
en tant que visiteur, je souhaite récupérer la liste des séries
en tant que contributeur, je souhaite ajouter une série
In progress 🤸
en tant que visiteur, je souhaite visualiser la fiche d'une série
Doing 🏗️
mise en place de la relation entre les entités Program et Category
Done ✅
création des entités Program et Category
création de la route /program/{id}
En base de données relationnelle, les différentes tables peuvent être liées entre elles. Les entités de ton application Symfony étant un reflet de ta base de données, il paraît pertinent de conserver cette association entre les différentes classes que sont tes entités.
Cette quête va t'apprendre à créer des relations entre tes différentes entités. Tous les liens doivent être renseignés au sein des différentes classes pour que l'ORM sache les interpréter. Pour cela, tu vas utiliser les attributs que tu as vus sur une quête précédente.
Sommaire
Définir la relation
Pour l'instant, ton projet Wild Series contient 2 entités : Program et Category. Dans ce cas, tu as déjà la solution. Tu vas utiliser une relation Many-To-One. Mais qu'est-ce que cela signifie précisément et existe-t-il d'autres types de relation@ ?
Il en existe effectivement d'autres :
- One-To-One
- One-To-Many
- Many-To-Many
avec des déclinaisons unidirectional, bidirectional, self-referencing. Tu verras toutes ces autres relations et déclinaisons dans de futures quêtes.
Que signifie précisément une relation Many-To-One ?
Cela suit la même logique que lorsque tu travailles sur la modélisation de base de données. La réponse va dépendre du besoin de l’application en cours de développement. Il faut réfléchir au lien entre tes entités. Dans le cas présent, Category et Program.
- Un
Programpeut-il appartenir à une ? ou plusieursCategory? - Une
Categorypeut-elle contenir un seul ? ou plusieursProgram?
Ce qui semble évident, c'est qu'une Category peut contenir plusieurs Program.
Par exemple, Walking Dead, American Horror Story et The Haunting of Hill House (qui sont des Program) font partie d’une seule catégorie “Horreur”.
Nous avons donc plusieurs Program pour une seule catégorie.
Diagramme de classe UML représentant une relation de composition entre nos deux classes.
Application
Il y a une certaine cohérence avec ce que tu faisais jusqu’à présent en gestion de base de données relationnelle. Mais il va falloir assimiler une nouvelle manière de penser.
Doctrine est un ORM et, comme tu l’as compris, le “O” veut dire Object. Et comme son nom l’indique, il ne fonctionne qu'avec des objets. Cela va donc se traduire par une nouvelle propriété category dans l'entité Program. Cette propriété n'aura pas un type "basique" (string, integer...) mais un type "Relationships” (Associations).
Du côté logiciel, il faudra donc penser dorénavant en terme d’interactions entre différents objets.
Cependant, rien ne change pour ton SGBD ! Il fonctionne de la même manière ! Ainsi au niveau base de données, cette relation "objet" sera traduite par une clé étrangère (category_id) dans la table program. Et comme d’habitude au niveau de la base de données, ça sera bien un integer qui sera stocké.
Il ne faut pas qu'il y ait de confusion à ce sujet : l'ORM gère des objets, la base de données gère des données "basiques" (integer, char, varchar, float...). Les entités que tu manipules et les tables sont très proches, mais il subsiste quand même certaines différences, qui font qu’on ne peut pas dire que “une entité = une table”.
Créer la relation dans Symfony
Afin de réaliser la première étape, Symfony t'aide encore grâce à sa console en te proposant de mettre à jour ton entité déjà existante.
Remarque
Tu pourrais tout faire à la main en modifiant directement tes classes, mais l'utilisation de la console te permet d'aller plus vite et d'éviter les erreurs.
symfony console make:entity
Lorsque tu vas indiquer en Class name le nom d’une entité déjà existante, Doctrine va comprendre que tu souhaites y ajouter une propriété. Ne te trompe pas en mettant un autre nom sinon doctrine créera une nouvelle entité !
`Class name of the entity to create or update (e.g. OrangeElephant)`:
> Program
Your entity already exists! So let's add some new fields!
`New property name (press <return> to stop adding fields)`:
> category
`Field type (enter ? to see all types)` [string]:
> ManyToOne
`What class should this entity be related to?`:
> Category
`Is the Program.category property allowed to be null (nullable)? (yes/no)` [yes]:
> no
`Do you want to add a new property to Category so that you can access/update Program objects from it - e.g. $category->getPrograms()? (yes/no)` [yes]:
> no
updated: src/Entity/Program.php
`Add another property? Enter the property name (or press <return> to stop adding fields)`:
>
`Success!`
Next: When you're ready, create a migration with `symfony console make:migration`
Et le résultat dans ta classe Program :
#[ORM\ManyToOne()]
#[ORM\JoinColumn(nullable: false)
private ?Category $category = null;
Il faut noter une chose importante. Lorsque Symfony te propose :
Do you want to add a new property to Category so that you can access/update Program objects from it - e.g. $category->getPrograms()? (yes/no) [yes]:
Il te demande en fait si tu souhaites que ta relation soit bidirectionnelle. Pour le moment, réponds no. La notion de bidirectionnalité arrivera dans une prochaine quête. Chaque chose en son temps !
Parce que l’”Entité”, ce n’est pas la ”table”.
Très bien, tu as rajouté la relation entre ces deux entités au sein de ton application. Mais garde en tête que ton entité n’est pas directement reliée à ta base de données.
N’oublie pas d’impacter sur ton SGBD toutes les modifications que tu effectues côté PHP sur tes classes d’entités. C'est l'étape de migration !
Pour cela, il te suffit d’exécuter deux commandes, présentées dans la quête précédente. Si tu ne les retrouves pas, la solution est juste en dessous, mais fais l'effort de chercher ;-)
Solution|||bash|||Commandes pour effectuer la migration|||0|||Hide
symfony console make:migration
symfony console doctrine:migrations:migrate
D’ailleurs, saurais-tu dire quel est l’impact sur la base de données, et quelle requête SQL va contenir ta classe de migration ? N'hésite pas à ouvrir la classe de migration générée par la commande pour y voir les requêtes effectuées lors de la migration.
💪 Challenge
Crée la relation ManyToOne
Ce challenge sera très simple, car tu as besoin des quêtes suivantes pour mettre en place des choses plus complexes. Il s'agira uniquement de contrôler visuellement le code de la classe Program. Cette dernière doit être conforme à ce qui est expliqué dans la quête. C’est-à-dire, une classe Program.php qui contient une propriété category paramétrée comme il se doit dans ses attributs avec les getter et setter associés.
Critères de validation
-
La propriété
categoryest présente et privée. -
Le getter et setter correspondants sont présents et publics.
-
Les attributs
ManyToOneetJoinColumnsont présentes et correctement paramétrées (nullable=false) -
Le
usenécessaire pour les attirbutes#[ORM]est en place. -
Une (et une seule) classe de migration est présente, ajoutant une clé étrangère côté
program. -
Le code à jour est disponible sur un repository GitHub