Cómo crear un framework web propio mediante componentes de Symfony (II)

25/09/2018
crear un framework web propio mediante componentes de Symfony

En el post anterior sobre 'Cómo crear un framework web propio mediante componentes de Symfony ', comenzamos el refactor de un proyecto en php plano para convertirlo en un desarrollo standard. En este post, vamos a continuar con ello y cambiar cosas más avanzadas dentro de un desarrollo web como el sistema de enrutado y una manera standard de trabajar con ficheros para cargar ficheros YML.

Todo el código puede ser consultado aquí y para probarlo puedes utilizar este proyecto de docker web.

 

Inclusión de un sistema de enrutado

El siguiente paso natural es mejorar un tema que en cualquier web le da un toque de profesionalidad y ayuda mucho al SEO de la página,  esto es el sistema de enrutado.

Antiguamente nos solíamos encontrar rutas que hacían referencia a ficheros dentro de la página o que llevaban muchos parámetros como por ejemplo las rutas que tenemos aún en nuestro sitio web:

Por ejemplo esta ruta en una aplicación moderna sería:

<Entorno>/person/1

Desapareciendo el parámetro get y ocultando a simple vista la tecnología con la que está realizado el sitio web.

Para realizar todo esto nos vamos a servir de varios componentes de symfony con los que seremos capaces de montar este sistema de enrutado.

Estos componentes son:

  • symfony/routing

Se trata de un paquete que nos proporciona una serie de clases y métodos para procesar las url pedidas por el frontend y poder definir el código que las procesa.

  • symfony/http-foundation

Se trata de un paquete de symfony que nos proporciona métodos y clases para trabajar con las peticiones y las respuestas del servidor.

  • symfony/http-kernel

Se trata de un paquete de symfony que nos proporciona un sistema central para procesar y ejecutar todas las peticiones que se realicen en nuestro sistema.

Al igual que en los anteriores puntos sólo deberemos introducir en el terminal:

composer require <paquete>

Lo primero que debemos hacer es crear un fichero .htaccess como este que le indique a apache que todas las peticiones vayan al index.php:

Inclusión de un sistema de enrutado

Este fichero lo crearemos en la raíz del proyecto.

Haciendo que nuestro fichero index.php sea el punto de partida de cualquier petición a nuestra web centralizamos todas las peticiones a un mismo punto y haremos que todo pase por el mismo manejador común.

 

El siguiente punto es crear una clase llamada CustomFrameworkHttpKernel con espacio de nombres customFramework\kernel en la carpeta <raíz proyecto>/src/kernel que sea la encargada del procesamiento central de todo nuestro código. Esta clase tendrá un método handle que será el encargado de procesar la solicitud y decidir a qué controlador enviarla.

 

Esta clase será instanciada y usada en nuestro fichero index.php quedando de la siguiente manera:

 

 crear una clase llamada CustomFrameworkHttpKernel

Básicamente hemos creado un objeto request a partir de las variables globales de php ($GLOBALS), hemos obtenido una instancia del kernel del que hemos hablado, hemos llamado al método handle para tratar la petición y enviamos la respuesta que nos devuelvan.

También hemos incluido un control de excepciones para mostrar en un template los errores devueltos por la aplicación, en caso de que exista alguno.

 

Lo siguiente que nos hace falta es un componente encargado de el enrutamiento y de trabajar con las rutas, para ello vamos a crear una clase llamada RoutingHelper con espacio de nombres customFramework\helper\routing en la carpeta <raíz-proyecto>/src/helper/routing. Esta clase tendrá un método llamado getCompleteRouteCollection  que será estático en el cuál definiremos los objetos Routes y RouteCollection the nuestra aplicación y que contendrán las rutas existentes en ella, además en cada una de ellas indicaremos el controlados y métodos que se van a encargar de la ejecución de cada una de ellas.

getCompleteRouteCollection

 

El siguiente paso es crear nuestra clase controladora que será la encargada de contener los métodos controladores para cada request (En una aplicación normal lo más lógico es que exista más de una clase controladora, ya que cada clase agrupa los métodos controladores de alguna funcionalidad en concreto, en este caso personas). Nuestra clase se va a llamar PersonsController con espacio de nombres customFramework\controller en el directorio <raíz-proyecto>/src/controller. Esta clase va a contener dos métodos indexAction y getPersonAction y en cada método se incluirá casi el mismo código que antes nos encontrábamos en cada fichero php (index.php y persons.php).

customFramework\controller

Cómo último paso nos queda borrar el fichero persons.php ya que ese fichero no va a volver a ser utilizado y su código ha pasado a estar en el controlador tal como hemos descrito.

Todo el código realizado en este punto puede verse en este commit.

 

Inclusión de sistema para trabajar con ficheros YML

Otro punto importante a la hora de desarrollar un sitio web suele ser el trabajo con ficheros, bien para cargar información o bien para crear logs por ejemplo.

Php dispone de múltiples herramientas para el trabajo con ellos pero en este punto vamos a introducir en nuestro sitio web varios componente de symfony:

 

  • symfony/config: Se trata de un componente que provee métodos y clases para trabajar con ficheros YML de configuración, por ejemplo para bases de datos.

  • symfony/finder: Se trata de un componente que provee métodos y clases para trabajar con ficheros y directorios.

  • symfony/yaml: Se trata de un componente que provee métodos y clases para cargar y guardar ficheros YML.

En este apartado vamos a utilizar estos componentes para el trabajo con ficheros YML y lo utilizaremos para cargar las configuraciones y parámetros de la base de datos y para cargar las rutas de nuestro RoutingManager.

Lo primero que necesitamos es realizar los composer require del mismo modo que hasta ahora en el directorio en el que se encuentra nuestro composer.json:

composer require <paquete>

Una vez tengamos las dependencias vamos a empezar a cargar las rutas de la web desde un fichero Yaml. Para ello vamos a crear un fichero persons.yml en la ruta <raíz-proyecto>/src/routing. En este fichero deberá verse así:

<raíz-proyecto>/src/routing

Tenemos dos rutas en él:

  • index: Esta ruta muestra que la url es la vacía (el index de la página) y que su controlador es el método indexAction de la clase PersonsController.

  • asignatura: Esta ruta muestra que la url es /person/id, dónde id es el identificador de la persona en la base de datos. También indica que su controlador es el método getPersonAction de la clase PersonsController. Además también indica que el id debe ser un valor numérico (Más información acerca de los requirements se puede encontrar aquí).

Una vez tengamos este fichero creado lo siguiente es utilizarlo en nuestro RoutingHelper para que devuelva las rutas cargadas desde ese fichero. Para ello vamos a hacer uso de los componentes de config y yaml que hemos cargado previamente quedando el método getCompleteRouteCollection así:

getCompleteRouteCollection

 

La otra tarea que vamos a desarrollar en este apartado es externalizar a un Yaml los parámetros de conexión de la base de datos, los cuales tenemos “Hard-coded” en nuestro código y no es una muy buena práctica.

Para realizar esto lo primero que vamos a hacer es crear un fichero Yaml en el que almacenaremos la configuración de conexión de la base de datos. Este fichero se llamará parameters.yml y lo crearemos en la carpeta <raíz-proyecto>/src/config y quedará así:

 <raíz-proyecto>/src/config

Este fichero lo añadiremos al fichero .gitignore para no comitearlo al repositorio ya que no deben guardarse variables de conexiones ni datos en repositorios.

En el mismo directorio vamos a crear un fichero similar, con las mismas variables, pero vacías que nos servirá de ejemplo. Este se llamará parameters.example.yml y servirá de modelo para poder crear el parameters.yml en algún otro entorno. Por supuesto este fichero si lo comitearemos ya que como se ha dicho se trata de un modelo.

Para hacer uso de este fichero en nuestra web necesitaremos crear una clase llamada ConfigHelper con espacio de nombres customFramework\helper\config  en el directorio <raíz-proyecto>/src/helper/config. Esta clase tendrá un método getConfigParameters que dada una configuración clave devuelve todos sus valores, database en este caso.

Por otro lado tendremos que modificar nuestra clase DoctrineHelper para utilizar los datos cargados por esta nueva clase, quedando así el método getEntityManager:

customFramework\helper\config

Todo el código realizado en este punto puede verse en este commit.

Algunos consejos y posibles ampliaciones

Ya hemos convertido nuestra web antigua en un portal moderno y con tecnología basada en componentes que añaden una capa de seguridad a nuestro portal. Aún así no está todo hecho, siempre hay huecos a posibles mejoras cómo:

  • Activar y configurar la consola de doctrine para poder realizar tareas desde la línea de comandos. Podemos encontrar cómo hacerlo en la documentación de doctrine.

  • Incluir tests que nos asegurasen que nuestro código funciona correctamente siempre. Podemos encontrar cómo hacerlo en la documentación de symfony.

  • luir un inyector de dependencias para poder incluir automáticamente los componentes que necesitemos. Para ellos podemos seguir la documentación de symfony.

Más información y referencias pueden ser consultadas aquí.