Symfony2 en action: la base de données avec Doctrine - part 3
Dans cet article je vais détailler la création des Entities qui vont me servir à construire ma base de données. Au programme Doctrine 2, les annotations. Pour rappel, la configuration de la base de données se trouve dans cet article.
1. Création du Bundle
Comme indiqué dans l’article précédent sur le choix de structure des bundles, j’ai choisis de séparer les Entity Doctrine dans un bundle spécifique: /src/Lp/LibBundle
J’ai créé le Bundle avec la ligne de commande de Symfony2 :
> php app/console generate:bundle
J’ai bêtement répondu « Yes » à toutes les questions et il me faut donc nettoyer un peu ce qu’a généré Symfony. Je ne change rien dans le fichier AppKernel.php mais par contre je supprime l’import des routes dans le fichier /app/config/routing.yml. Voici les lignes que je supprime :
LpLibBundle: resource: "@LpLibBundle/Resources/config/routing.yml" prefix: /
Dans le dossier du bundle je supprime tout sauf la classe LpLibBundle.php et le dossier DependencyInjection et le fichier Resources/config/services.yml.
Je crée ensuite un dossier Entity dans lequel je mettrai les Entities de Doctrine et un dossier Repository dans lequel je mettrai les Repositories de Doctrine. Pour rappel, les classes Repositories permettent de regrouper toutes les requêtes SQL d’une table dans un seul fichier et ainsi ne pas polluer le controller.
2. Model de la base de données
Mon model est relativement simple:
3. Créations des Entities Doctrine
Bien que j’ai configuré mon Bundle pour qu’il prenne en charge la config au format Yaml, je peux utiliser les annotations pour configurer mes Entities. C’est certainement le seul endroit dans le projet où je vais utiliser les annotations. La raison est simple, il ne semble pas possible (je ne l’ai pas vu mais je n’ai pas testé non plus) de créer un fichier Yaml unique pour tout le schema de la base de donnée. De plus je trouve la structure des Yaml imposée par Doctrine trop compliquée par rapport à ce qui existe pour Propel. C’est donc l’occasion de tester les annotations, d’autant plus qu’il s’agit d’une fixture de Doctrine.
a. Entête des class
Pour chacune de mes entités, je spécifie explicitement le nom de la table car le nom de mes Entities commence par une majuscule, mais je ne veux avoir que des minuscules dans ma base de données.
Je déclare également un Repository à chaque fois.
Mes fichiers ressembleront donc à :
<?php namespace Lp\LibBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * Class mappant la table groupe de la bdd * * @author ulrich, 07/09/11 * * @ORM\Entity * @ORM\Table(name="groupe") * @ORM\Entity(repositoryClass="ALp\LibBundle\Repository\GroupeRepository") */ class Groupe { }
b. Déclaration des primary key
Problème de la clé primaire des tables: je n’aime pas nommé les clés primaires simplement « id » car en cas de jointure il y a un risque de se tromper. Dans ma base de données, les clés primaires seront donc préfixées du nom de la table. Mais cela pose un potentiel problème. Si je veux utiliser la génération des CRUD, ce qui devrait être le cas pour l’admin, il faut absolument que la primary key de l’Entity se nomme « id ». J’ai donc utlisé l’option name dans l’annotation Column pour toutes mes clés primaires.
/** * @author ulrich, 07/09/11 * * @ORM\Id * @ORM\Column(name="groupe_id", type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id;
c. Les relations entre les tables
Je n’ai que des relations de type One to Many et très honnêtement je trouve la déclaration des relations vraiment pas simple.
Prenons le cas de la relation entre les tables Groupe et Pays. Je veux une relation de type bidirectionnelle, que je puisse trouver le pays d’un groupe mais aussi tous les groupes d’un pays.
Pour cela je suis obligé de déclarer la relation dans les deux Entities Groupe et Pays.
Dans Pays, il faut créer une variable qui servira à stocker les groupes qui y sont rattaché. Cette variable doit absolument être de type Doctrine\Common\Collections\ ArrayCollection, je devrai donc l’initialiser dans le constructeur de la classe.
/** * @ORM\OneToMany(targetEntity="Groupe", mappedBy="pays_id") */ protected $groupes; public function __construct() { $this->groupes = new ArrayCollection(); }
L’annotation est importante, elle définit le sens de la relation 1 pays = plusieurs groupe du point de vue de l’Entity Pays et référence l’Entity et la variable associé. Attention, ne pas indiquer le nom des champs de la base de données.
Dans Groupe, il faut créer une variable qui représente la clé étrangère.
/** * @ORM\ManyToOne(targetEntity="Pays", inversedBy="groupes") * @ORM\JoinColumn(name="pays_id", referencedColumnName="pays_id") */ protected $pays_id;
Cette fois il y a deux annotations. La première est semblable à celle définis dans l’Entity Pays pour la variable $groupes mais du point de vue de l’Entity Groupe cette fois ci. D’où l’utilisation de ManyToOne et non de OneToMany.
La deuxième annotation, permet de définir la construction de la jointure SQL. Et cette fois il faut bien utiliser le nom des champs dans la base et non le nom des variables de l’Entity. Rappelez-vous mes clés primaire n’ont pas le même nom dans les Entities et dans la bdd.
d. Validation du schema
Doctrine dans sa version standalone propose en ligne de commande l’outil doctrine orm:validate-schema pour vérifier qu’il n’y a pas d’erreur dans la déclaration des Entities.
Malheureusement, SensioLabs n’a pas semblé utile de porter cette commande dans Symfony.
C’est donc l’occasion de jouer un peu avec la création de Console/Commande Line plus connu sous le nom de Task dans sf1. Vous trouverez le détail de l'extension de la ligne de commande de Symfony2 dans cet article.
Pour conclure, je trouve que la création des Entities est vraiment une tache fastidieuse et peut être source d’un nombre d’erreur incroyable. N’ayant utilisé que Propel jusqu'à présent, je trouve cette tache vraiment compliqué avec Doctrine et je sais d’avance que je vais y passer des jours quand je devrai créer un modèle plus complexe. Au final je n’ai pas essayé la ligne de commande, j’aurai peut-être dû. A voir pour les Entity des utilisateurs que je ferai plus tard.
Pour ceux que ça intéresse vous pouvez télécharger le bundle que j’ai créé avec toutes les Entities et les Repository.
Ajouter un commentaire