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 Request – Response, 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:
- Creamos una tabla llamada medata cuyos columnas son: name, slug, y los metatags, title, description, etc.
- 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
- Agregamos el nuevo listener a services.yml
- Extendimos el Controller, incorporamos la variable metatags, y sobre-escribimos los métodos render y renderView. Obviamente, todos los controladores implicados extendían de BaseController en lugar de Controller
- 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 1 – Thin 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.
Ha 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.
Jornadas Symfony 2011
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.
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.
El patrón Strategy
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…





