19 - Symfony : EntityType
Objectifs
- ✅ Découvrir l'EntityType
- ✅ Savoir l'implémenter
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 qu'administrateur, je souhaite ajouter des acteurs à une série
Doing 🏗️
mise en place de l'EntityType
Done ✅
création de l'entité Actor
Tu as déjà découvert les formulaires dans Symfony, et quelques-uns des types de champs disponibles (TextType, ChoiceType, etc.). Dans cette quête, tu vas en découvrir un nouveau, l’EntityType.
Sommaire
🤔 EntityType : qu'est-ce que c'est ?

Il arrive souvent que tu aies besoin de charger dans un formulaire, les données d'une autre entité (dans l'exemple ci-dessus, la liste des catégories dans un formulaire d'ajout de série). Symfony embarque cette fonctionnalité nativement, grâce aux EntityType. En réalité, l’EntityType n’est rien d’autre qu’un ChoiceType dans lequel des valeurs par défaut sont déjà remplies, ces valeurs provenant de la BDD et étant chargées via Doctrine.
Regarde pour l’instant la partie “Basic Usage”.
🏗 Implémenter un EntityType
Mise en place du formulaire
Tu vas maintenant reprendre le projet Wild Séries. Dans l’entité Program, tu as une propriété $actors qui a été ajoutée lors de la quête précédente. Cette propriété est au pluriel car une série est reliée à plusieurs acteurs.
Dans une autre quête, tu as généré le CRUD associé à l’entité Program. Malheureusement pour toi, l’ajout de la relation entre Actor et Program a eu lieu après la génération du CRUD. La classe ProgramType n’est donc plus complète. Mais pas de panique, cette quête est conçue pour la mettre à jour.
Dans ProgramType, tu vas donc ajouter un champ ”actors” de type EntityType comme ci-dessous :
// add the 2 use
use App\Entity\Actor;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
// here, your form (title, summary…)
$builder->add('actors', EntityType::class, [
'class' => Actor::class,
'choice_label' => 'name',
'multiple' => true,
'expanded' => false,
]);
Tu remarques que l’EntityType nécessite 2 paramètres obligatoires :
class : permet d’indiquer quelle entité est reliée au champ actors. Au niveau de l’entité Program, $actors devra en effet être un objet de type Actor.
Le but de l’EntityType est d’aller récupérer en base de données toutes les données reliées à l'Entity Actor (via Doctrine) et ensuite afficher ces données dans un select (par défaut). Or, l’entité Actor contient deux champs (id et name). Pour savoir lequel de ces deux champs il doit afficher dans le select, Symfony se base sur le choice_label, qui doit donc correspondre au nom d’une des propriétés de l’entité Actor.
Quand tu écris 'choice_label' => 'name' c’est en réalité le getter getName() qui est appelé. Pour afficher quelque chose de plus complexe dans le select (par exemple concaténer les valeurs de plusieurs champs de la BDD), tu peux créer un getter personnalisé, qui se chargera d’effectuer la mise en forme plus complexe, puis l’appeler dans le choice_label. Comme par exemple, mettre 'choice_label' => 'selector' qui renverrait vers la méthode :
public function getSelector(): string
{
return $this->getId() . ' - ' . $this->getName();
}
Tu trouveras un récapitulatif des différentes manières de définir le choice_label : par un string correspondant au nom d’un getter, mais également via une fonction anonyme (callable).
Si tu vas sur ta page d’édition ou de création d’une série, tu peux voir qu’un nouveau champ ”actors” est apparu sous forme de liste déroulante.
Il existe de nombreux paramètres supplémentaires qui sont quant à eux optionnels :
-
expandedetmultiple: ces deux paramètres (qui existent également dans le ChoiceType) prennent true ou false. Par défaut, ils sont tous les deux à false et les données de catégories sont affichées sous la forme d’un select. Mais tu pourrais très bien vouloir les afficher sous la forme de boutons radios ou de checkboxes, en fonction de tes besoins. Ce tableau de la documentation officielle résume bien les 4 cas de figures, en fonction des valeurs de expanded et multiple. -
query_builder: Ici, par défaut, la requête effectuée pour récupérer les informations en base de données est un simple SELECT * FROM category (toutes les catégories sont affichées). Or, parfois, tu aimerais trier tes résultats (ORDER BY), n’en afficher qu’une partie (LIMIT), ou n’en afficher que certaines lignes selon une condition (WHERE). L’optionquery_builderpermet de personnaliser la requête SQL qui sera exécutée pour récupérer les valeurs en BDD. Tu peux regarder la documentation à ce sujet, mais tu verras le fonctionnement des requêtes personnalisées un peu plus tard.
Attends une minute, une série peut être associée à plusieurs acteurs, et un acteur peut être associé à plusieurs séries, une simple liste déroulante classique n’est donc pas la meilleure manière d’afficher ce sélecteur. Tu dois donc rajouter les propriétés expanded et multiple à true pour avoir une interface cohérente avec nos besoins. Rafraîchis ta page, cette fois-ci tu dois voir apparaître des cases à cocher multiples. Il serait aussi possible de configurer expanded à false et multiple à true pour avoir une liste déroulante à choix multiple, mais ce type de champ de formulaire est un peu moins utilisé de nos jours.
Tu peux créer une nouvelle série et cocher à loisir autant d’acteurs que tu le souhaites et cliquer sur “Save” ce qui va enregistrer en base de données ta série et te rediriger sur la page de listing des séries.
Ensuite, retourne sur la page d’édition de la série que tu viens de modifier, que constates-tu ? Ta série est bien saisie, mais pas les acteurs que tu viens de sélectionner. Que s’est il passé ? Tu peux trouver la réponse dans cet article: Saving the Inverse Side of a ManyToMany avec la solution à ton problème dans le chapitre d’après : Form by_reference + Adder and Remover
Pourquoi le côté “inverse” n'est-il pas sauvegardé ?
La solution à ton problème un peu plus bas dans ta page.
📜 Traitement du formulaire
Côté traitement du formulaire, tu n’as rien à modifier ! Mais comment cela est-il possible ? Reprends un peu le déroulement du traitement de formulaire dans Symfony :
-
Le formulaire est soumis, l’objet
Requestest récupéré au niveau du contrôleur. -
La méthode
$form->handleRequest($request)hydrate automatiquement l’objetProgramavec les données saisies dans le formulaire (et donc contenues dans$request). -
La méthode
setActors()deProgramest donc appelée et attend un tableau d’objetsActor. Or le champactorsdu formulaire est un EntityType, relié justement à la classeActor. Symfony arrive donc à faire automatiquement le lien et à hydrater correctement l’objetProgram.
👓 Et la vue dans tout ça ?
Côté Twig, là encore, tu n’as absolument rien à faire. Tu avais indiqué que tu souhaitais simplement afficher ton formulaire. Les champs que tu ajoutes du côté de la classe ProgramType se mettent donc automatiquement à jour dans ta vue.
Si tu souhaitais faire des mises en page un peu plus complexes au niveau de ton formulaire, tu serais sans doute amené à ajouter quelques lignes dans ce fichier Twig, mais tu verras cela dans une future quête. Pour un affichage simple, tout se fait automatiquement !
💪 Challenge
Ajoute un champ Actors
En suivant les instructions de la quête, fais en sorte que le champ actors apparaisse sous forme d’une suite de cases à cocher sur le formulaire d’ajout d’une nouvelle série. Il doit afficher les noms des acteurs contenus dans la base.
🧐 Critères de validation
- Le formulaire d’ajout d’une série affiche un champ acteur (EntityType),
- Ce champ apparaît sous la forme de cases à cocher.
- Lorsque l’on ajoute un acteur à une série, celui-ci est bien relié à la série en BDD.
- Les acteurs s'affichent sur la page de la série.
- Le code est disponible sur un repository GitHub, avec une branche correspondant à cette quête.