lunes, 26 de noviembre de 2012

Control simple de las páginas visitadas en tu sitio Symfony2

En esta ocasión os voy a mostrar como utilizar el sistema de enrutado de Symfony2 y los Listeners para que utilizando una simple tabla podamos controlar el número de veces que se hace click en un enlace o se muestra una página.

El concepto es sencillo, mediante una configuración específica le indicamos al bundle que rutas queremos que "escuche" para poder registrar ese evento en la tabla, acto seguido se muestra el enlace original.

Básicamente esto se resuelve en el onKernelRequest del Listener:
public function onKernelRequest(GetResponseEvent $event)
{
    $route = $event->getRequest()->attributes->get('_route');
    
    // this intercepts the click
    if( isset($this->routesNames[$route]) ){
    
        // aqui va el codigo que procesa el click
    
    }
}

Evidentemente para poder comprobar la ruta con las que queremos procesar necesitamos rellenar el array routesNames, esto lo haremos el constructor del mismo listener, y aprovecharemos la posibilidad que nos ofrece Symfony2 para inyectar por dependencia las rutas a procesar, que estarán en el config.yml.
#every time a route is visited
jaitec_click:
    routes:
        # this is a logic name and can be anything
        demo:
            # this match to logic route (not pattern) defined in routing.yml
            route: _demo_hello
            # the type in wich is save at jaitec_click table
            entity_type: demo
            # this is for no id, the route contains a name or slug
            entity_id: ~
            entity_name: name
        another:
            route: _demo
            entity_type: demo
            # the route not contains any info because is a fixed route
            entity_id: 1
            entity_name: ~

Como podéis ver he puesto de ejemplo dos de las rutas que vienen con la instalación básica de Symfony2, como os decía en la declaración del servicio inyectamos las rutas de config.yml
services:
    jaitec_click_request_listener:
        class: Jaitec\ClickBundle\Listener\JaitecClickListener
        arguments:
            router: @router
            container: @service_container
            routes: %jaitec_click.routes%
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

Ahora ya por fin podemos ver el constructor que recibe esos parámetros
public function __construct( RouterInterface $router, Container $container, $routes )
{
    $this->router       = $router;
    $this->container    = $container;
    $this->routes       = $routes;
    $this->routesNames  = array();
    foreach($routes as $name=>$route){
            $this->routesNames[$route['route']] = $name;
    }
}

Lo demás os lo podéis imaginar, rellenar un registro en la tabla para la página o enlace visitado o si ya existe incrementar el número de click hechos.

Para rematar el bundle he añadido un controller que permite integrar la vista de las veces que se ha visto la página en nuestras propias vistas:

{% extends "AcmeDemoBundle::layout.html.twig" %}

{% block title "Hello " ~ name %}

{% block content %}

Hello {{ name }}!

Esta página se ha visto {% render "JaitecClickBundle:View:clicksShow" with {'entity_type':'demo', 'entity_id':null, 'entity_name':name} %} {% endblock %}


El controller que hace esto es tan sencillo como ésto:
public function clicksShowAction( $entity_type, $entity_id, $entity_name )
{
    $em          = $this->getDoctrine()->getEntityManager();
    $click       = $em->getRepository('JaitecClickBundle:Click')->findOneBy(
                                    array(
                                        'entity_type'   => $entity_type,
                                        'entity_id'     => $entity_id,
                                        'entity_name'   => $entity_name,
                                         )
                                    );

    $times = $this->get('translator')->trans('times');
    $time  = $this->get('translator')->trans('time');

    if(!$click){
        $clicks = 0;
        $msg    = 'any '.$times;
    }else{
        $clicks = $click->getClicks();
        if($clicks==1){
            $msg = 'one '.$time;
        }else{
            $msg = $clicks . ' ' . $times;
        }
    }

    return $this->render("JaitecClickBundle::include.html.twig",array(
        'clicks'    => $clicks,
        'msg'       => $msg,
    ));

}


El resto del código lo podéis descargar de aquí

7 comentarios:

  1. Cándido Hernández1 de junio de 2014, 2:16

    Excelente artículo que me ayuda mucho a comprender por donde debo tirar. "Sólo" hay un problema para mi caso particular, y es que estoy trabajando con una BBDD NoSQL, MongoDB concretamente. ¿Sabes como extrapolar esta información para su implementación en Mongo? Me sería de mucha ayuda.

    Gracias,
    Cándido Hernández

    ResponderEliminar
  2. Gracias Cándido por tus elogios.
    Me parece muy interesante lo que comentas y ya que ahora estoy utilizando MongoDB no me costaría mucho hacer una continuación del artículo en esta línea, esta semana publico algo en este sentido.

    ResponderEliminar
  3. Cándido Hernández1 de junio de 2014, 13:20

    Eso estaría genial. Estaré atento a las publicaciones.

    ResponderEliminar
  4. Hola, he actualizado el bundle para que se pueda instalar en versiones más recientes de SF y se pueda configurar para usar con MongoDB o MySQL.
    https://github.com/jlaso/visit-control-bundle
    Además lo he añadido a una estructura básica que tengo para probar lo de las traducciones, para que se vea en funcionamiento.
    https://github.com/jlaso/tradukoj-symfony2-demo
    En cuanto encuentre algo de tiempo preparo un artículo completo.

    ResponderEliminar
  5. Hola Cándido, me he confundido y he comentado más abajo.

    ResponderEliminar
  6. Esta solución es buena pero en un caso de que el usuario visita muchas veces una pagina en un intervalo corto de tiempo y no quisiera que esas visitas cuenten que tendría que hacer?

    ResponderEliminar
  7. Hola Daymer, primero que nada, muchas gracias por comentar.
    Para el caso que comentas yo establecería unas reglas de negocio: primero guardas la IP del visitante y compruebas en cada visita que hace de nuevo si es desde la misma IP y si han transcurrido al menos x segundos desde la última visita.
    Sería una buena mejora para el bundle descrito. Lo cual te invito a hacer.
    Gracias de nuevo.

    ResponderEliminar