Este foro ya no está activo, así que no puedes publicar nuevas preguntas ni responder a las preguntas existentes.

Como puedo hacer un registro en una entidad cuando algún usuario logea a mi aplicación? Symfony 2.8

20 de mayo de 2016

Estoy intentando generar un log automático para lo que registro algunos datos dependiendo de las acciones realizadas en la aplicación, necesito registrar los inicios de sesión para lo que hice un event-listener:

<?php
namespace AppBundle\EventListener;
 
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use AppBundle\Entity\Log;
 
class SecurityListener extends Controller{
 
    public function __construct(Security $security, Session $session) 
    {
        $this->security = $security;
        $this->session = $session;
    }
 
    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
        $log = new Log();
        $em = $this->getDoctrine()->getManager();
        $log->setCategoria('SESION');
        $log->setTipo('LOGIN');
        $log->setFecha(strftime('%Y-%m-%d'));
        $log->setHora(strftime('%H:%M'));
        $log->setAccion('Se ha iniciado sesión');
        $em->persist($log);
        $em->flush();        
    }
}

y este es mi config.yml

parameters:
    locale: en
    account.security_listener.class: AppBundle\EventListener\SecurityListener
 
services:
    account.security_listener:
        class: %account.security_listener.class%
        arguments: ['@security.token_storage', '@session']
        tags:
            - { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin }

AL momento de logear me arroja el siguiente error:

Catchable Fatal Error: Argument 1 passed to AppBundle\EventListener\SecurityListener::__construct() must be an instance of Symfony\Component\Security\Core\Security, instance of Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage given, called in C:\Users\Cristopher\Desktop\AppInventario\app_inventario\app\cache\dev\appDevDebugProjectContainer.php on line 302 and defined


Respuestas

#1

Hola @MauriWilde,

El error se produce porque en el servicio estás inyectando el @security.token_storage pero en tu constructor definiste como primer parámetro Security $security.

Otra cosa que veo es, no se porque extiendes de la clase controller si es para hacer persistencia deberías inyectar el Object_manager o el entity_manager directamente desde el servicio.

Y por último, este es uno de esos casos en que yo inyectaría el Service_container completo, me disculparan si no es la mejor práctica, pero.....

public function __construct(ContainerInterface $container, Session $session)
    {
        $this->container = $container;
        $this->session = $session;
    }
 
    public function onInteractiveLogin(InteractiveLoginEvent $event)
    {
        $user = $event->getAuthenticationToken()->getUser();
 
        ...
    }

El $user sería para obtener el usuario logueado, ya teniendo eso puedes hacer la persistencia a la base de datos.

Espero que sea de ayuda,

Saludos,

@miguelplazasr

20 mayo 2016, 4:17
#2

Muchas gracias por tu ayuda @miguelplazasr

gracias a ella me di cuenta de los errores que estaba cometiendo, y pude reparar el funcionamiento, te comento como lo resolví:

Modifiqué los servicios.

services:
    account.security_listener:
        class: %account.security_listener.class%
        arguments: ['@security.context', '@doctrine']
        tags:
            - { name: 'kernel.event_listener', event: 'security.interactive_login' }

Y finalmente creé esta clase, la aplicación solo utilizara una sesión de administrador por lo que no necesito rescatar el usuario.

<?php
namespace AppBundle\EventListener;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Doctrine\Bundle\DoctrineBundle\Registry as Doctrine;
use AppBundle\Entity\Log;
 
class SecurityListener{
 
    private $securityContext;
    private $em;
 
    public function __construct(SecurityContext $securityContext, Doctrine $doctrine) 
    {
        $this->securityContext = $securityContext;
        $this->em = $doctrine->getEntityManager();
    }
 
    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
        if ($this->securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
            $log = new Log();
            $log->setCategoria('SESION');
            $log->setTipo('LOGIN');
            $log->setFecha(strftime('%Y-%m-%d'));
            $log->setHora(strftime('%H:%M'));
            $log->setAccion('Se ha iniciado sesión');
            $this->em->persist($log);
            $this->em->flush();     
        }
    }
}

Espero que la información le sirva a alguien más que necesite realizar una acción similar, saludos.

@MauriWilde

20 mayo 2016, 16:35
#3

Bueno ya veo que lo resolviste, pero ya te estaba respondiendo dejo mi solución en un proyecto hecho por mi.

<?php
namespace UserSecurityBundle\Listener;
 
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\DependencyInjection\ContainerInterface;
use UserSecurityBundle\Entity\Log;
 
/**
 * Custom login listener.
 */
class LoginListener
{
 
    private $container;
 
    /**
     * Constructor
     * 
     * @param ContainerInterface $container
     */
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }
 
    /**
     * Do the magic.
     * 
     * @param  Event $event
     */
    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
    {
        $em = $this->container->get('doctrine')->getManager();
 
        $entity = $event->getAuthenticationToken()->getUser();
        $entity->setLastConexion(time());
        $em->persist($entity);
 
        $name = $entity->getUsername();
        $log = new Log();
        $log->setIp($this->container->get('request')->getClientIp());
        $log->setShowText("Se inició la sesión del usuario {$name}.");
        $log->setOperation('Inicio de sesión');
        $log->setUsername($name);
        $log->setEntityId($entity->getId());
        $em->persist($log);
        $em->flush();
    }
}

y finalmente el servicio que llama a esto por supuesto:

services:
    login_listener:
        class: UserSecurityBundle\Listener\LoginListener
        arguments: ['@service_container']
        tags:
            - { name: kernel.event_listener, event: security.interactive_login }

Adáptalo a tu situación pero la idea de un listener es esa.

Suerte, espero haber ayudado.

@jdvellon

20 mayo 2016, 16:41