Symfony2 en action: la base de données avec Doctrine2 - suite
L'article précédent sur la création des Entity de Doctrine2 dans Symfony2, ne suffit finalement pas pour démarrer. J'ai rencontré plusieurs problèmes comme la suppression en cascade dans la base de donnée ou encore l'utilisation de fonction avancé.
1. Modification en cascade
a. Dans MySQL
Sur les contraintes de clé étrangère, il est possible de déclencher une action lors de la mise à jour ou de la suppression de cette clé. Les options onDelete et onUpdate que l’on trouve dans le langage SQL.
Il est possible de spécifier des valeurs pour ces deux options dans les annotations des Entity de Doctrine2. Cela se fait dans l’annotation de la clé étrangère.
/** * @ORM\ManyToOne(targetEntity="Pays", inversedBy="groupes") * @ORM\JoinColumn(name="pays_id", referencedColumnName="pays_id", onDelete="set null", onUpdate="cascade") */ protected $pays_id;
b. Dans Doctrine2
Dans un autre registre, il est possible de gérer les modifications en cascade des Entity. Celle qui me parait la plus intéressante est l’option ‘persist’. Elle permet de lier les Entity entre elles et de ne pas avoir à toutes les spécifier lors de la persistance.
Sans l’option cascade = {« persist »}
$oVille = new Ville(); $oVille->setNom(‘Paris’); $oSalle = new Salle(); $oSalle->setNom(‘Le Bataclan’); $oSalle->setVilleId($oVille); $this->oEM->persist($oVille); $this->oEM->persist($oSalle); $this->oEM->flush();
En spécifiant l’option cascade dans l’annotation de la relation entre l’Entity salle et ville, il est possible de persister les deux objets en base sans avoir à les spécifier tous les deux.
/** * @ORM\ManyToOne(targetEntity="Ville", inversedBy="salles", cascade={"persist"}) * @ORM\JoinColumn(name="ville_id", referencedColumnName="ville_id", onDelete="cascade", onUpdate="cascade") */ protected $ville_id;
Ce qui permet d’écrire :
$oVille = new Ville(); $oVille->setNom(‘Paris’); $oSalle = new Salle(); $oSalle->setNom(‘Le Bataclan’); $oSalle->setVilleId($oVille); $this->oEM->persist($oSalle); $this->oEM->flush();
Attention, la gestion en cascade n’est que dans un sens. Dans mon exemple je l’ai spécifié dans la relation Salle -> Ville. Cela implique qu’avec le code ci-dessus l’objet Salle ne sera pas enregistré en base si je spécifie l’objet Ville à la place de Salle dans la commande persist(). Si vous voulez persister vos éléments sans avoir à vous soucier duquel va déclencher la cascade, il faut définir la cascade dans toutes les relations OneToMany mais aussi ManyToOne.
2. Doctrine2 éxtensions
Doctrine propose toutes les clés pour ajouter des behaviors mais ne semble pas en fournir par défaut. J’ai dû me rabattre sur des bundles de la communauté : DoctrineExtensionBundle qui utilise DoctrineExtension.
L’installation est relativement simple, n’utilisant pas git dans mon projet, j’ai dû télécharger les deux paquets et les décompresser dans le dossier /vendor et de mettre à jour les fichiers autoload.pphp et appKernel.php. Pour plus d’info sur l’installation de ces deux paquets, allez voir cette page.
a. Behavior Timestampable
Ce behavior permet de mettre à jour une colonne de type, date, time ou datetime sur un évènement : création, mise à jour ou une mise à jour conditionnel d’un champs. Mais seules les deux premières options m’intéressent.
Il faut commencer par activer le behavior dans le fichier app/config/config.yml
stof_doctrine_extensions: default_locale: en_US orm: default: timestampable: true
Ensuite il ne reste plus qu’à compléter les Entity en ajoutant une colonne pour la création et une colonne pour la mise à jour. Ne pas oublier d’ajouter dans les namespace la class de gestion des annotations des Extensions Doctrine2.
<?php namespace Lp\LibBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; use Doctrine\Common\Collections\ArrayCollection; /** * @ORM\Entity * @ORM\Table(name="concert") * @ORM\Entity(repositoryClass="ALp\LibBundle\Repository\ConcertRepository") */ class Concert { … /** * * @Gedmo\Timestampable(on="create") * @ORM\Column(type="datetime") */ protected $created_at; /** * * @Gedmo\Timestampable(on="update") * @ORM\Column(type="datetime") */ protected $updated_at;
b. Behavior Sluggable
Comme pour le béhvior précédent, il faut commencer par l’ajouter à la configuration dans /app/config/config.yml
stof_doctrine_extensions: default_locale: en_US orm: default: timestampable: true sluggable: true
J’ai ajouté un champs titre et un champs slug dans la table concert. Le champs slug étant l’équivalent du champs titre une fois passé dans l’algorithme du behavior. Voir ci-dessus pour la déclaration de Gedmo dans les namespace à utiliser.
/** * * @Gedmo\Sluggable * @ORM\Column(type="text") */ protected $titre; /** * * @Gedmo\Slug * @ORM\Column(type="text") */ protected $slug;
J’utilise le comportement par default du behavior slug, ce qui implique que le champs sera mis à jour si je modifie le titre.
Réponse de Ulrich le 19 déc. 2012