Symfony2: création d’un service
Bonjour, Nous allons voir dans cet article la mise en place d’un callback sur une entité dans symfony2. Contexte Nous allons ici prendre l’exemple d’un site de...
Filter by Category
Bonjour, Nous allons voir dans cet article la mise en place d’un callback sur une entité dans symfony2. Contexte Nous allons ici prendre l’exemple d’un site de...
Toi l’ami qui travaille dans le Web, rejoins nous à l’apéro Web de Montpellier : Date : Jeudi 8 Septembre Lieu : au Fitzpatrick’s Irish Panicked. Luckily to...
Un problème récurrent pour beaucoup de développeurs utilisant Symfony est l’éternel conflit avec l’utilisateur « www-data » lors des accès, par exemple, aux fichiers...
Fêtons de lancement de Symfony2 au Shakespeare A partir de 19H00 The Shakespeare Pub 12 Rue de la Petite Loge, 34000 Montpellier 04 67 60 22 25
Pour calculer les distances entre deux points géographiques il faut se baser sur leurs latitudes et longitudes. Lorsque l’on souhaite appliquer ce calcul en SQL on se...
Après 2 ans et demi de bons et loyaux services, le thême trouvé rapidement sur internet s’en est allé et laisse place à une version plus personnelle de l’équipe de...
Lexik est une agence Web spécialisée dans le développement d’application Web et de sites Internet sur-mesure en PHP avec le framework Symfony. Nous souhaitons renforcer nos...
Bonjour,
Nous allons voir dans cet article la mise en place d’un callback sur une entité dans symfony2.
Nous allons ici prendre l’exemple d’un site de commande. Chaque commande a un statut et nous souhaitons enregistrer dans la base de données chaque changement de statut afin de conserver un historique.
Pour cela, Symfony2 et Doctrine fournissent un ensemble d’actions pouvant être appelées à chaque étapes du cycle de vie d’une entité (« lifecycle »).
Vous trouverez à cette adresse l’ensemble des événements disponibles: Lifecycle Events
Dans notre cas, nous avons choisi d’effectuer la sauvegarde du statut après chaque mise à jour de notre entité Order (qui gère les commandes du site). Pour cela nous allons utiliser l’événement postUpdate qui sera appelé après chaque update de l’entité.
Voici, les deux objets concernés par ce service:
L’objet commande
/** * Projet\OrderBundle\Entity\Order * * @ORM\Entity(repositoryClass="Projet\OrderBundle\Repository\OrderRepository") * @ORM\Table(name="Projet_order_order") * @ORM\HasLifecycleCallbacks */ class Order { /** * @var integer $id * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @var Yatoo\UserBundle\Entity\User $user * * @ORM\ManyToOne(targetEntity="Yatoo\UserBundle\Entity\User", inversedBy="orders") * @ORM\JoinColumn(name="user_id", referencedColumnName="id") */ protected $user; /** * @var string $comment * * @ORM\Column(name="title", type="string", length=255) */ protected $title; /** * @var decimal $price * * @ORM\Column(name="price", type="decimal", length="7", scale="2") */ protected $price; /** * @var integer $quantity * * @ORM\Column(name="quantity", type="integer") */ protected $quantity; /** * @var Yatoo\CardBundle\Entity\Pricing $pricing */ public $pricing; /** * @var smallint $status * * @ORM\Column(name="status", type="smallint") */ protected $status; /** * @var text $comment * * @ORM\Column(name="comment", type="text", nullable="true") */ protected $comment; /** * @ORM\OneToMany(targetEntity="HistoricalStatus", mappedBy="order", cascade={"all"}) * @ORM\JoinColumn(name="order_id", referencedColumnName="id") */ protected $historicalStatus; /** * @Gedmo\Timestampable(on="create") * @ORM\Column(name="created_at", type="datetime") */ protected $createdAt; protected $statusOld; ... } |
Et HistoricalStatus qui servira à stocker l’historique des changements de statut des commandes.
/** * Projet\OrderBundle\Entity\HistoricalStatus * * @ORM\Entity(repositoryClass="Projet\OrderBundle\Repository\HistoricalStatusRepository") * @ORM\Table(name="Projet_order_historical_status") * @ORM\HasLifecycleCallbacks */ class HistoricalStatus { /** * @var integer $id * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @var object $order * * @ORM\ManyToOne(targetEntity="Order", inversedBy="files") * @ORM\JoinColumn(name="order_id", referencedColumnName="id") */ protected $order; /** * @var smallint $status * * @ORM\Column(name="status", type="smallint") */ protected $status; /** * @var text $comment * * @ORM\Column(name="comment", type="text", nullable="true") */ protected $comment; /** * @Gedmo\Timestampable(on="create") * @ORM\Column(name="created_at", type="datetime") */ protected $createdAt; ... } |
Dans un premier temps, il faut créer le dossier Listener dans votre bundle.Dans notre exemple ce dossier se trouvera à l’adresse : repSite/src/Projet/OrderBundle/Listener puis créer le fichier OrderListener.php.
Voici pour exemple le code de notre service:
namespace Projet\OrderBundle\Listener; use Projet\OrderBundle\Entity\HistoricalStatus; use Projet\OrderBundle\Entity\Order; use Doctrine\ORM\Event\LifecycleEventArgs; class OrderListener { public function postUpdate(LifecycleEventArgs $args) { $entity = $args->getEntity(); $entityManager = $args->getEntityManager(); if ($entity instanceof Order) { if($entity->getOldStatus() != $entity->getStatus()) { $historicalStatus = new HistoricalStatus(); $historicalStatus->setOrder($entity); $historicalStatus->setComment($entity->getComment()); $historicalStatus->setStatus($entity->getStatus()); $entityManager->persist($historicalStatus); $entityManager->flush(); } } } } |
Pour expliquer rapidement ce que fait ce bout de code :
Si le statut de la commande a changé, on crée un nouveau statut historique et on l’enregistre.
La fonction $entity->getOldStatus() permet de récupérer l’ancien statut de la commande avant sa mise à jour. La valeur est initialisée à la création de l’objet dans le constructeur.
Une fois votre listener écrit, il faut le configurer pour qu’il soit appelé. Pour cela, éditer simplement le fichier : Projet\OrderBundle\Ressources\config\services.xml et ajoutez y ces lignes:
<service id="order.postUpdate" class="Projet\OrderBundle\Listener\OrderListener"> <tag name="doctrine.event_listener" event="postUpdate"/> </service> |
Vous pouvez aussi le déclarer en yml :
services: order.postUpdate : class: Projet\OrderBundle\Listener\OrderListener tags: - { name: doctrine.event_listener, event: postUpdate } |
Comments
Tu ne pouvais pas simplement créer une méthode dans ton modèle Order avec une annotation
/** @postUpdate */ ?
Très utile comme d’habitude, merci 🙂
@David
Bonjour, en utilisant le postUpdate, je n’ai pas accès à l’entityManager pour sauvegarder l’historique. Il faut donc utiliser un listener via les services.
Pour initialiser la valeur statusOld il faudrait plutôt passer par un listener sur postLoad, car quand on récupère un objet depuis la bdd le constructeur n’est pas appelé
Et il n’y a pas moyen, lors d’un update, de savoir automatiquement quelle était l’ancienne valeur, sans avoir à gérer à la main un champ « old » ?
Excellent ce tutoriel ! C’est un service qui se déclenche tout seul, un système de trigger grosso modo… A l’inverse de services qui sont destinés être appelés dans les controleurs (type doctrine ou mailer).
Et niveau performance ? Il me semble bien que le listener est appelé à chaque Update d\’entité, chose qui peut faire largement faire perdre en performance si on a une application qui sollicite souvent doctrine, à utiliser donc avec précaution !