Skip to content
ene 4 13

Generando metatags para todas las vistas mediante eventos en Symfony2

by David

Estos días, junto a Rodrigo (@menrod), hemos tratado de dar una solución a una cuestión que se nos ha planteado en el trabajo. Os la contamos para ver si tenéis una mejor solución o si queréis aportar algún comentario.

Status

Nuestro cliente necesitaba poder personalizar los metas de cada página: title, description, keywords, etc. Debido a que teníamos un gran cantidad de acciones y controladores, queríamos evitar ir por cada acción de cada controlador agregando la lógica que generaría las variables “meta” a pasar a la vista.

Solución aplicada

Una vez descartada la posibilidad de agregar la lógica a todos los controladores, lo siguiente que pensamos fue interferir en el proceso de RequestResponse, y hacer uso del componente EventDispatcher que trae Symfony2, para introducir nuestra lógica justo antes de que se ejecute cualquier controlador. Symfony2 trae unos filtros (Before, After) que nos ayudan en esta labor.

Nuestro objetivo fue, por tanto, que todas las vistas tuviesen unas variables por defecto, los metatags, cuyos valores dependían de la petición, de ahí que no nos valiese simplemente utilizar el config.yml, aunque puede ser un lugar donde predefinir unos valores por defecto.

La vista era fácilmente actualizable, simplemente implicaba agregar unas lineas al layout base.

Explotando kernel.controller event

Este evento es disparado por el core de Symfony2 (FilterControllerEvent) justo antes de actuar cualquier controlador, por lo que creemos que es el lugar idóneo para inicializar todos los metatags que queríamos pasar a las vistas.

De la idea al código

Decidimos crear un controlador base (BaseController.php) el cual extiende del controlador que trae Symfony2 (Controller.php). Este controlador base tiene un atributo nuevo, metatags, el cual es un array que contiene todos los metas necesarios (title, description, etc). Estos metatags son los que se inicializan desde MetadataListener. Además, sobre-escribimos los métodos render y renderView para que todas las vistas reciviesen por defecto nuestra variable metatags.

Pasos que seguimos:

  1. Creamos una tabla llamada medata cuyos columnas son: name, slug, y los metatags, title, description, etc.
  2. Creamos un listener MetadataListener al cual inyectamos EntityManager para obtener e inicializar los metatags de la base de datos. Este listener escucha a kernel.controller
  3. Agregamos el nuevo listener a services.yml
  4. Extendimos el Controller, incorporamos la variable metatags, y sobre-escribimos los métodos renderrenderView. Obviamente, todos los controladores implicados extendían de BaseController en lugar de Controller
  5. Actualizamos nuestro layout

Tabla Metadata

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
Tu\Namespace\A\Entity\Metadata:
  type: entity
  repositoryClass: Tu\Namespace\A\Entity\MetadataRepository
  table: metadata
  fields:
    id:
      type: integer
      id: true
      generator:
        strategy: AUTO
    name:
      type: string
      length: 500
    slug:
      type: string
      length: 500
    metatitle:
      type: text
      length: null
    metadescription:
      type: text
      length: null
    metakeywords:
      type: text
      length: null

  indexes:
    slug_index:
      columns: [slug]
      type: unique

Clase MetadataListener

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
namespace Tu\Namespace\A\Listener;
 
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Doctrine\ORM\EntityManager;
use Tu\Namespace\A\Controller\BaseController;
 
class MetadataListener
{
    private $entityManager;
 
    function __construct(EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
    }
 
    public function onKernelController(FilterControllerEvent $event)
    {
        $controller = $event->getController();
        if (!is_array($controller)) {
            return;
        }
 
        if ($controller[0] instanceof BaseController) {
            $myController = $controller[0];
 
            // Nosotros nos basamos en la URL solicitada para obtener la info
            $slug = $event->getRequest()->getPathInfo();
            $metadata = $this->entityManager->getRepository('TuBundle:Metadata')->findOneBy(array('slug' => $slug));
 
            if ($metadata) {
                $myController->setMetas(array(
                    'metadescription' => $metadata->getMetadescription(),
                    'metatitle' => $metadata->getMetatitle(),
                    'metakeywords' => $metadata->getMetaKeywords()
                ));
            }
        }
    }
}

Actualizando el services.yml

1
2
3
4
5
miproyecto.listener.metadata:
    class: Tu\Nampespace\A\Listener\MetadataListener
    arguments: [ '@doctrine.orm.entity_manager' ]
    tags:
      - { name: kernel.event_listener, event: kernel.controller, method: onKernelController }

Clase BaseController.php

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
namespace Tu\Namespace\A\Controller;
 
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
 
class BaseController extends Controller {
 
    private $metatags = array();
 
    public function getMetas() {
        return $this->metatags;
    }
 
    public function setMetas($metas) {
        $this->metatags = $metas;
    }
 
    public function renderView($view, array $parameters = array())
    {
        return parent::renderView($view, \array_merge($this->metatags, $parameters));
    }
 
    public function render($view, array $parameters = array(), Response $response = null)
    {
        return parent::render($view, \array_merge($this->metatags, $parameters), $response);
    }
 
}

Pros

  • Unificamos toda la lógica de gestión de los metatags en un solo método

No se me ocurre mucho más a parte de lo citado antes, lo cual nos provoca también un “thin controller“. (Thin Controller 1Thin Controller 2)

Contras

  • Como siempre se pregunta uno en Symfony2, ¿es el lugar más correcto?
  • Rendimiento: ¿realmente afecta al rendimiento? Cada petición pasará por nuestro Listener ejecutando nuestra lógica, aunque si la petición no debe ser atendida por nuestros controllers es descartada.
  • Estamos provocando una consulta a la base de datos por cada Request que precise los metatags: Esto puede ser una sobrecarga innecesaria, aunque si el cliente desea poder editar esta información desde el backend no tenemos muchas alternativas.
  • Nuestra solución se basa en la URL (Slug) para obtener los metatags, por lo que ¿nos hacemos vulnerables o difícilmente sostenibles antes continuos cambios en la estructura de rutas (routing)?

Parece que todo son contras, pero el hecho de centralizar la lógica y abstraer a todos los controladores lo veo una ventaja.

jul 11 11

deSymfony 2011 un grupo de gente inquieta

by David

deSymfony 2011Ha pasado una semana desde las Jornadas Symfony 2011 en deSymfony y la verdad es que desde la organización estamos muy contentos por el “feedback” recibido. En ninguna de nuestras predicciones nos imaginábamos la buena acogida que ha tenido y la respuesta positiva de la gente.

La verdad que podría enfocar el post desde un punto de vista organizativo, que si retraso por aquí …, que si problemas con no se que …, pero prefiero enfocarlo desde un punto de vista más humano y la agradable sorpresa que fue poder conversar con tanta gente que no conocía y que ese día tuve el placer de conocer y “desvirtualizar”.

He visto muchas cosas que me han gustado, por ejemplo he visto gente que ha vuelto con las pilas cargadas y enseguida se han puesto a programar con Symfony2, con Git, con Twig, u otros lenguajes y tecnologías que se iban comentando en pequeños círculos que se hacían durante los descansos, comidas o cenas: Erlang es un claro ejemplo.

read more…

jun 16 11

Jornadas Symfony 2011

by David

Este año volvemos a la carga, parece que fue ayer cuando nos embarcamos en las primeras Jornadas Symfony. Ha pasado un año y la segunda edición está a punto de comenzar.

Es de agradecer el empeño e ilusión de gente como, Javier Eguiluz, Nacho Martín, Javier López, Marcos Labad, Albert Jessurum. Todos ellos han puesto su empeño no sólo en la organización del evento, sino preparando la aplicación deSymfony que se mostrará el primer día.
Tened en cuenta que Symfony2 todavía no tiene versión estable, y que la documentación cambia constantemente y supone un esfuerzo mantenerse al día. A pesar de estos handicaps nos van a impartir unas charlas magistrales sobre nuestro framework favorito para que todos podamos empezar a trabajar con la última versión de éste.

read more…

oct 21 10

IV Encuentro de programadores Java: RIA (Cliente Rico)

by David

Tras unos meses de inactividad, se activa el ciclo de jornadas técnicas gratuitas con el IV Encuentro de Programadores Java organizado por Oscar Belmonte (profesor del Departamento de Lenguajes y Sistemas Informáticos) y decharlas.com. En esta ocasión, la temática de las jornadas girará entorno al desarrollo de aplicaciones con interfaz de cliente rico.

El evento se realizará el 28 y 29 de Octubre en la Universitat Jaume I de Castellón, tienes toda la información del evento: horarios, ponentes y formulario de inscripción, en la web de decharlas.

oct 13 10

El patrón Strategy

by David

En internet existen varios sitios donde se explican los patrones de diseño con sumo detalle, por ejemplo:

La intención de este post es asentar los conocimientos adquiridos en un curso sobre patrones de diseño que estoy realizando (octubre y noviembre del 2010).
El patrón Strategy es un patrón que se engloba dentro de los conocidos como patrones de comportamiento.
El patrón Strategy nos permite definir varios algoritmos para un mismo método, de tal manera que podamos escoger en cada momento cómo queremos que se comporte nuestra clase cliente.
read more…