Horodatage automatique des Entity

Dans une base de données c’est toujours intéressant de savoir quand un enregistrement a été créé et modifié et par qui.

Avec symfony et doctrine c’est très simple : nous allons utiliser les listener.

Il faut d’abord ajouter 4 zones sur les entités sur lesquelles vous voulez les infos de traçage :

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="dateAjout", type="datetime")
     */
    private $dateAjout;

    /**
     * @ORM\ManyToOne(targetEntity="User")
     * @ORM\JoinColumn(name="userAjout_id", referencedColumnName="id", nullable=true)
     */
    private $userAjout;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="dateModif", type="datetime")
     */
    private $dateModif;

    /**
     * @ORM\ManyToOne(targetEntity="User")
     * @ORM\JoinColumn(name="userModif_id", referencedColumnName="id", nullable=true)
     */
    private $userModif;

La notion nullable=true permet d’avoir null dans cette zone, ce qui permet par exemple de tracer les enregistrements créés et modifiés par des utilisateurs anonyme.

La commande magique qui va créer les getters et setters :

php bin/console doctrine:generate:entities AppBundle

Pour mettre à jour la base de données il y a 2 choix, si les entity que vous allez mettre à jour ne contiennent pas de données, alors super facile :

 php bin/console doctrine:schema:update --force

Par contre si elles contiennent des données, il faut modifier le script généré, sinon vous allez avoir un message d’erreur du style « Constraint violation », pour récupérer le script :

php bin/console doctrine:schema:update --dump-sql

Le script généré et modifié (ajout de « DEFAUT ‘1900-01-01 » à 2 endroits) :

ALTER TABLE nomTable ADD dateAjout DATETIME NOT NULL DEFAULT '1900-01-01', ADD dateModif DATETIME NOT NULL DEFAULT '1900-01-01', ADD userAjout_id INT DEFAULT NULL, ADD userModif_id INT DEFAULT NULL;
ALTER TABLE nomTable ADD CONSTRAINT FK_D21EDEA365B081F FOREIGN KEY (userAjout_id) REFERENCES fos_user (id);
ALTER TABLE nomTable ADD CONSTRAINT FK_D21EDEA60B27A3 FOREIGN KEY (userModif_id) REFERENCES fos_user (id);
CREATE INDEX IDX_D21EDEA365B081F ON nomTable (userAjout_id);
CREATE INDEX IDX_D21EDEA60B27A3 ON nomTable (userModif_id);

Il faut ensuite créer la classe qui mettra à jour ses zones, par exemple AppBundle\EventListener\MajInfosEntityListener, cette classe doit comporter 2 fonctions : prePersist et preUpdate

prePersit se déclenchera avant tout persist (insert)

preUpdate se déclenchera avant tout flush (update)

namespace AppBundle\EventListener;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;

class MajInfosEntityListener
{

    private $token_storage;

    public function __construct(TokenStorageInterface $token_storage)
    {
        $this->token_storage = $token_storage;
    }


    // Permet de mettre à jour les zones dateajout, datemodif, useajout, usermodif de toutes les tables
    // A chaque fois qu'un INSERT est fait.
    public function prePersist(LifecycleEventArgs $args)
    {

        $entity = $args->getEntity();

        if (property_exists($entity, "dateAjout")) {
            $entity->setDateAjout(new \DateTime("now"));
            $entity->setDateModif(new \DateTime("now"));
        }

        if (property_exists($entity, "userAjout")) {
            $user = $this->token_storage->getToken()->getUser();
            $entity->setUserAjout($user);
            $entity->setUserModif($user);
        }

    }

    // Permet de mettre à jour les zones dateajout, datemodif, useajout, usermodif de toutes les tables
    // A chaque fois qu'un UPDATE est fait.
    public function preUpdate(LifecycleEventArgs $args)
    {

        $entity = $args->getEntity();

        if (property_exists($entity, "dateModif")) {
            $entity->setDateModif(new \DateTime("now"));
        }

        if (property_exists($entity, "userModif")) {
            $user = $this->token_storage->getToken()->getUser();
            $entity->setUserModif($user);
        }

    }
}

Il ne reste plus qu’à déclarer la classe comme service dans le fichier AppBundle/app/config/services.yml :


services:
# service_name:
# class: AppBundle\Directory\ClassName
# arguments: ["@another_service_name", "plain_value", "%parameter_name%"]

maj_entity_user:
class: AppBundle\EventListener\MajInfosEntityListener
arguments:
- "@security.token_storage"
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: preUpdate }

Have fun !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *