IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Symfony PHP Discussion :

Journalisation des manipulations CRUD sur les différentes Entity (récupération d'informations système)


Sujet :

Symfony PHP

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2014
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2014
    Messages : 10
    Points : 8
    Points
    8
    Par défaut Journalisation des manipulations CRUD sur les différentes Entity (récupération d'informations système)
    Bonjour, je suis entrain de développer une application à l'aide du Framework Symfony2. En ce moment j'aimerais journaliser les les manipulations des différentes tables afin de les persister et en avoir un historique.

    Pour ce faire, j'ai d'abord configuré mon fichier config.yml en ajoutant un service et un listener basé sur un événement onFlush comme suit :

    ...
    services:
    LoggingListener:
    class: compagnie\CRUDBundle\Listener\LoggingListener
    tags:
    - { name: doctrine.event_listener, event: onFlush, method: onFlush }

    Ensuite, j'ai créé une classe LoggingListener dans le répertoire compagnie\CRUDBundle\Listener comme ci-dessous.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    namespace compagnie\CRUDBundle\Listener;
     
    use Doctrine\ORM\Event\OnFlushEventArgs;
    use compagnie\CRUDBundle\Entity\SysLog;
     
    class LoggingListener {
     
    /**
    * @param \Doctrine\ORM\Event\OnFlushEventArgs $ea
    */
    public function onFlush(OnFlushEventArgs $eventArgs) {
    $em = $eventArgs->getEntityManager();
     
    $eventManager = $em->getEventManager();
     
    // Remove event, if we call $this->em->flush() now there is no infinite recursion loop!
    $eventManager->removeEventListener('onFlush', $this);
     
    $uow = $em->getUnitOfWork();
     
    foreach ($uow->getScheduledEntityInsertions() AS $entity) {
    $this->createLog($em, 'INSERT', $entity);
    echo "INSERT";
    }
     
    foreach ($uow->getScheduledEntityUpdates() AS $entity) {
    $this->createLog($em, 'UPDATE', $entity);
    echo "UPDATE";
    }
     
    foreach ($uow->getScheduledEntityDeletions() AS $entity) {
    $this->createLog($em, 'DELETE', $entity);
    echo "DELETE";
    }
     
    //Re-attach since we're done
    $eventManager->addEventListener('onFlush', $entity);
    }
     
    private function createLog($em, $action, $entity) {
     
    $syslog = new SysLog;
     
    $syslog->setInstant(new \DateTime);
    $syslog->setAction($action);
    $syslog->setTablename($em->getClassMetadata(get_class($entity))->getTableName());
    $syslog->setEntity(get_class($entity));
     
    $syslog->setController('');
    $syslog->setData('');
    $syslog->setSysUserId('');
     
    $em->persist($syslog);
    $em->flush();
    }
    }
    Bien entendu, l'Entity SysLog existe et possède ses attributs ainsi que ses méthodes Setters et Getters. Mes questions concerne l'affectation des valeurs dans la méthode createLog($em, $action, $entity) pour les éléments ci-dessous :

    • $syslog->setController('');
      Comment puis-je récupérer le nom du contrôleur en question d'où provient l'action?
    • $syslog->setData('');
      Comment puis-je récupérer tous les attributs de l'entité avec ses valeurs en les regroupant par exemple avec un delimiter?
      Exemple : attr1:val1;attr2:val2;attr3:val3;...
      Un attribut peut également être une ArrayCollection...
    • $syslog->setSysUserId('');
      Et la dernière question, surtout, comment je peux récupérer l'identifient (ID) de l'utilisateur connecté?


    Merci d'avance de votre aide

  2. #2
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Comment puis-je récupérer le nom du contrôleur en question d'où provient l'action?
    Tu ne peux pas, ton listener n'est pas supposé connaitre le contrôleur.

    Comment puis-je récupérer tous les attributs de l'entité avec ses valeurs en les regroupant par exemple avec un delimiter?
    En faisant une méthode privée qui sérialise chacun des champs de ton entity dans un tableau et qui te retourne un implode de celui-ci.

    Et la dernière question, surtout, comment je peux récupérer l'identifient (ID) de l'utilisateur connecté?
    Tu ne peux pas, ton listener n'est pas supposé connaitre le security context.

    Ton listener, il est comme les contrôleurs : il aime pas trop le code.
    Tu peux appeler des services par contre. Mais pas faire ce traitement dans ton listener parce que du coup si tu as besoin comme dans ton cas du contrôleur ou du security context, ça lui rajoute des dépendances inutiles.

    Ce que je te suggère est d'ajouter une seule dépendance à un service qui va faire tout ce bazar.
    Et c'est ce service à qui tu injecteras l'objet Request (pour obtenir le contrôleur concerné), et le securityContext.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    use compagnie\CRUDBundle\Entity\SysLog;
    use path/to/Logger;
     
    class LoggingListener {
     
        private $logger;
     
        public function __construct(Logger $logger) {
            $this->logger = $logger;
        }
     
        /**
        * @param \Doctrine\ORM\Event\OnFlushEventArgs $ea
        */
        public function onFlush(OnFlushEventArgs $eventArgs) {
     
            //...
     
            foreach ($uow->getScheduledEntityInsertions() AS $entity) {
                $this->logger->createLog('INSERT', $entity);
                echo "INSERT";
            }
     
            //...
        }
    }
    Et ton service Logger à qui on injecte, la request, l'entity manager et le security context aura plus ou moins cette gueule là

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    use path/to/SecurityContext;
    use path/to/Request;
    use path/to/EntityManager;
     
    class Logger {
     
        private $em;
        private $request;
        private $securityContext
     
        public function __construct(EntityManager $em, Request $request, SecurityContext $securityContext) {
            $this->em = $em;
            $this->request = $request;
            $this->securityContext = $securityContext;
        }
     
        public function createLog($action, $entity)
        {
            // create log...
        }
    }
    Si je ne raconte pas de bêtise, tu dois pouvoir récupérer le nom du contrôleur via l'objet Request, et le user depuis le securityContext (ça j'en suis sûr).
    J'ai pas creusé plus que ça les méthodes et tout mais le principe c'est ça. Tu injectes l'objet dont tu as besoin pour récupérer les valeurs dont tu as besoin : mais tu les injectes à ton service, pas à ton listener. La seule injection de dépendance que tu fais à ton listener c'est celle du service.

    Je te laisse gérer pour ce qui est de la configuration de la DI.

    ++

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2014
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2014
    Messages : 10
    Points : 8
    Points
    8
    Par défaut
    Tout d'abord, merci pour ta réponse. Lorsqu'un flush est déclanché depuis un contrôleur, j'accède directement à la méthode onFlush(OnFlushEventArgs $eventArgs) de la classe LoggingListener n'est ce pas? Comment je dois faire pour fournir les bons paramètres dans le constructeur étant donné que j'accède directement à la méthode mentionné? Même question pour la classe Logger et son constructeur.

    J'ai également vu que nous pouvons fournir des arguments dans un service dans fichier config.yml. Par exemple :

    services:
    LoggingListener:
    class: compagnie\CRUDBundle\Listener\LoggingListener
    arguments: ['@security.context']
    tags:
    - { name: doctrine.event_listener, event: onFlush, method: onFlush }

    Comment cela fonctionne concretement?

  4. #4
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Concrètement : c'est là toute la puissance de Symfony l'injection de dépendances.

    En déclarant ton LoggingListener en tant que service (comme tu l'as fait dans un fichier .yml par exemple), tu vas pouvoir lui injecter des paramètres lorsque Symfony (en interne) va instancier cette classe. Tu n'as pas besoin de faire toi même $logger = new Loogger()

    Si tu as besoin de l'entityManager pour faire un flush par exemple, il te suffit de le rajouter en argument dans la définition du service (dans le fichier .yml en l'occurence).

    Dans ce fichier de config donc, tu dois déclarer ton service "LoggingListener" et lui donner en argument le service Logger.
    Tu dois aussi déclarer le service Logger et lui passer en argument les autres éléments que j'ai mis dans le constructeur de ta classe Logger.

    Tu devrais pouvoir faire plus ou moins ça, je te laisse retrouver les bonnes clés de service (tu as déja la bonne pour le security context) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    services:
        LoggingListener:
            class: compagnie\CRUDBundle\Listener\LoggingListener
            arguments: ['@mybundle_logger']
            tags:
                - { name: doctrine.event_listener, event: onFlush, method: onFlush }
     
        mybundle_logger:
            class: path\to\Logger
            arguments: 
                - ['@servicekey_Entitymanager']
                - ['@servicekey_Request']
                - ['@security.context']
    Important : les arguments et les paramètres du constructeur doivent correspondre et être dans le même ordre (comme je l'ai fait pour le logger). Sinon tu vas avoir des erreurs de type xxx expected yyy given.

  5. #5
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2014
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2014
    Messages : 10
    Points : 8
    Points
    8
    Par défaut
    J'ai enfin compris le fonctionnement des Services et Listeners, merci .

    Suite aux clés trouvés lors des mes recherches Google, j'ai configuré mon fichier config.yml comme suit :

    services:
    LoggingListener:
    class: compagnie\CRUDBundle\Listener\LoggingListener
    scope: request
    arguments: ['@LoggerService']
    tags:
    - { name: doctrine.event_listener, event: onFlush, method: onFlush }

    LoggerService:
    class: compagnie\CRUDBundle\Service\Logger
    scope: request
    arguments: [@doctrine.orm.entity_manager, @request, @security.context]

    Et j'obtiens l'erreur suivante que je ne comprend pas, lorsque j'accède à l'application :

    1/1 ScopeWideningInjectionException: Scope Widening Injection detected: The definition "doctrine.dbal.default_connection.event_manager" references the service "logginglistener" which belongs to a narrower scope. Generally, it is safer to either move "doctrine.dbal.default_connection.event_manager" to scope "request" or alternatively rely on the provider pattern by injecting the container itself, and requesting the service "logginglistener" each time it is needed. In rare, special cases however that might not be necessary, then you can set the reference to strict=false to get rid of this error.

    Et lorsque je clear le cache :

    [Symfony\Component\DependencyInjection\Exception\ScopeWideningInjectionException]
    Scope Widening Injection detected: The definition "doctrine.dbal.default_co
    nnection.event_manager" references the service "logginglistener" which belo
    ngs to a narrower scope. Generally, it is safer to either move "doctrine.db
    al.default_connection.event_manager" to scope "request" or alternatively re
    ly on the provider pattern by injecting the container itself, and requestin
    g the service "logginglistener" each time it is needed. In rare, special ca
    ses however that might not be necessary, then you can set the reference to
    strict=false to get rid of this error.

  6. #6
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    L'injection de la requête est un cas un peu particulier que je n'ai pas beaucoup pratiqué : je ne peux pas beaucoup plus t'aider, mais à priori je ne connais pas d'autre service capable de te retourner le nom de ton controller.

    Teste déjà en retirant cette dépendance : tu auras au moins les deux autres éléments dont tu as besoin.

    Je t'avouerai que je n'ai pas d'autre élément de réponse, mais je pense que tu n'es plus très loin et qu'il suffit de creuser encore un peu.
    Une fois tes services injectés tu pourras récupérer proprement chacune des infos dont tu as besoin.

    Bon courage

  7. #7
    Nouveau membre du Club
    Inscrit en
    Décembre 2008
    Messages
    62
    Détails du profil
    Informations forums :
    Inscription : Décembre 2008
    Messages : 62
    Points : 38
    Points
    38
    Par défaut
    Peut être que je suis HS, mais ça devrait pas rejoindre ton cas : https://github.com/simplethings/EntityAudit ???

  8. #8
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    725
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Juin 2011
    Messages : 725
    Points : 1 050
    Points
    1 050
    Par défaut
    Bonjour,

    Pour l'injection de l'objet Request dans un service, il y a une nouveauté dans la version 2.4
    http://symfony.com/blog/new-in-symfo...-request-stack

Discussions similaires

  1. Réponses: 4
    Dernier message: 07/08/2012, 15h00
  2. [AC-2000] Mise à jour des frontales sur les différents postes
    Par gravier3000 dans le forum IHM
    Réponses: 4
    Dernier message: 29/09/2010, 18h49
  3. Rôle des users et restrictions sur les différents schémas
    Par WildInTheWoods dans le forum Débuter
    Réponses: 8
    Dernier message: 18/11/2008, 12h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo