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.

Il y a 2 commentaires.

Ecrit par François Deléglise le 19 déc. 2012

Bonjour, Tout d'abord, merci pour votre article. Pour mon opération, je souhaite désactiver le timestampable uniquement sur la mise à jour d'un champ : - je met en place un compteur de vue sur une page, j'incrémente donc mon count en base de données, le updatedAt se voit donc mis à jour, ce que je ne veux pas. Une idée ? merci !

Réponse de Ulrich le 19 déc. 2012

Bonsoir,

Pas vraiment d'idée sur comment controller le behavior timestampable. Mais une solution simple est de mettre à jour la base via une requête SQL sans passer par la couche doctrine:

 
$pdo = $this->getDoctrine()->getManager()->getConnection();
$pdo->execute('......');

 

Ajouter un commentaire