Taming Slim 2.0

Slim es un marco liviano que tiene mucho impacto por su pequeña huella. Tiene un increíble sistema de enrutamiento y ofrece una base sólida para trabajar sin interponerse en su camino. Deja que te enseñe!

Pero eso no quiere decir que Slim no tenga algunos problemas; su configuración de un solo archivo se desordena a medida que crece su aplicación. En este artículo, revisaremos cómo estructurar una aplicación Slim no solo para mantener, sino también mejorar su funcionalidad y mantener las cosas ordenadas y sistemáticas..


Vanilla Slim

Comencemos mirando un código Slim común para identificar el problema. Después de instalar Slim a través de Composer, necesita crear una instancia de Delgado Objetar y definir sus rutas:

 get ('/', function () echo "Home Page";); $ app-> get ('/ testPage', function () use ($ app) $ app-> render ('testpage.php');); $ app-> run ();

Vamos a convertir el objeto Slim en el "controlador".

La primera llamada de método establece una nueva ruta para el URI raíz (/), y conecta la función dada a esa ruta. Esto es bastante detallado, pero fácil de configurar. La segunda llamada de método define una ruta para el URI página de prueba. Dentro del método suministrado, utilizamos Slim's. hacer() método para hacer una vista.

Aquí está el primer problema: esta función (un cierre) no se llama en el contexto actual y no tiene forma de acceder a las funciones de Slim. Es por esto que necesitamos usar el utilizar palabra clave para pasar la referencia a la aplicación Slim.

El segundo tema proviene de la arquitectura de Slim; está destinado a ser definido todo en un archivo. Por supuesto, puede subcontratar la variable a otro archivo, pero solo se ensucia. Idealmente, queremos la capacidad de agregar controladores para modularizar el marco en componentes individuales. Como beneficio adicional, sería bueno que estos controladores ofrecieran acceso nativo a las funciones de Slim, eliminando la necesidad de pasar referencias a los cierres.


Un poco de ingeniería inversa

Es discutible si la lectura del código fuente de un proyecto de código abierto se considera ingeniería inversa, pero es el término con el que me quedo. Entendemos cómo usar Slim, pero ¿qué sucede debajo del capó? Veamos una ruta más complicada para llegar a la raíz de esta pregunta:

 $ app-> get ('/ users /: name', function ($ name) echo "Hello". $ name;);

Esta definición de ruta usa dos puntos con la palabra, nombre. Este es un marcador de posición, y el valor utilizado en su lugar se pasa a la función. Por ejemplo, / usuarios / gabriel coincide con esta ruta, y 'gabriel' se pasa a la función. La ruta, / usuarios, por otro lado, no es una coincidencia porque falta el parámetro.

Si lo piensa de manera lógica, hay una serie de pasos que deben completarse para procesar una ruta..

  • Paso uno: compruebe si la ruta coincide con el URI actual.
  • Segundo paso: extraer todos los parámetros de la URI.
  • Paso tres: llamar al cierre conectado y pasar los parámetros extraídos..

Para optimizar mejor el proceso, Slim (utilizando devoluciones de llamada de expresiones regulares y grupos) almacena los marcadores de posición mientras comprueba las coincidencias. Esto combina dos pasos en uno, dejando solo la necesidad de ejecutar la función conectada cuando Slim está listo. Queda claro que el objeto de la ruta es autónomo y, francamente, todo lo que se necesita.

En el ejemplo anterior, teníamos acceso a las funciones de Slim al analizar las rutas, pero necesitábamos pasar una referencia de objeto Slim porque, de lo contrario, no estaría disponible dentro del contexto de ejecución de la función. Eso es todo lo que necesita para la mayoría de las aplicaciones, ya que la lógica de su aplicación debe ocurrir en el controlador.

Con eso en mente, extraigamos la parte de "enrutamiento" en una clase y convirtamos el objeto Slim en el "controlador".


Empezando

Para comenzar, descarguemos e instalemos "vanilla Slim" si aún no lo ha hecho. Voy a suponer que tienes Composer instalado, pero si no, sigue los pasos .

Dentro de un nuevo directorio, cree un archivo llamado compositor.json, y anexar lo siguiente:

 "name": "nettuts / slim-mvc", "require": "slim / slim": "*", "slim / extras": "*", "twig / twig": "*"

En una ventana de terminal, navegue a dicho directorio y escriba instalación del compositor. Le guiaré a través de estos paquetes, si es la primera vez que usa Slim.

  • delgado / delgado - el marco real de Slim.
  • delgado / extras - Un conjunto de clases opcionales para extender Slim..
  • ramita / ramita - El motor de plantillas Twig.

Técnicamente no necesitas los extras Slim o Twig para este tutorial, pero me gusta usar Twig en lugar de las plantillas estándar de PHP. Sin embargo, si usa Twig, necesita los extras de Slim, ya que proporciona una interfaz entre Twig y Slim.

Ahora vamos a agregar nuestros archivos personalizados, y comenzaremos agregando un directorio a la vendedores carpeta. Nombraré el mío Nettuts, Pero no dudes en nombrar el tuyo como desees. Si aún se encuentra en el terminal, asegúrese de que la ventana de su terminal esté en el directorio del proyecto y escriba lo siguiente:

mkdir vendedor / Nettuts

Ahora edita compositor.json agregando la referencia a esta nueva carpeta:

 "name": "nettuts / slim-mvc", "require": "slim / slim": "*", "slim / extras": "*", "twig / twig": "*", " carga automática ": " psr-0 ": " Nettuts ":" vendedor / "

Queremos que nuestra aplicación cargue automáticamente las clases del Nettuts espacio de nombres, así que esto le dice a Composer que mapee todas las solicitudes de Nettuts a la norma PSR-0 a partir de la vendedor carpeta.

Ahora ejecuta:

compositor dump-autoload

Esto vuelve a compilar el autocargador para incluir la nueva referencia. A continuación, crea un archivo, llamado Router.php, dentro de Nettuts directorio, y escriba lo siguiente:

  

Vimos que cada objeto de ruta tiene una función independiente que determina si coincide con el URI proporcionado. Entonces, queremos una serie de rutas y una función para analizarlas. También necesitaremos otra función para agregar nuevas rutas y una forma de recuperar el URI de la solicitud HTTP actual.

Comencemos agregando algunas variables miembro y el constructor:

 Class Router rutas protegidas $; solicitud protegida de $; función pública __construct () $ env = \ Slim \ Environment :: getInstance (); $ this-> request = new \ Slim \ Http \ Request ($ env); $ this-> route = array (); 

Establecemos el rutas variable para contener las rutas, y la solicitud variable para almacenar el Slim Solicitud objeto. A continuación, necesitamos la posibilidad de agregar rutas. Para seguir las mejores prácticas, dividiré esto en dos pasos:

función pública addRoutes ($ rutas) foreach ($ rutas como $ ruta => $ ruta) $ método = "cualquiera"; if (strpos ($ path, "@")! == false) list ($ path, $ method) = explode ("@", $ path);  $ func = $ this-> processCallback ($ path); $ r = new \ Slim \ Route ($ route, $ func); $ r-> setHttpMethods (strtoupper ($ method)); array_push ($ this-> rutas, $ r); 

Esta función pública acepta una matriz asociativa de rutas en el formato de ruta => ruta, dónde ruta es una ruta Slim estándar y camino es una cadena con la siguiente convención:

Opcionalmente, puede omitir ciertos parámetros para utilizar un valor predeterminado. Por ejemplo, el nombre de la clase será reemplazado por Principal si lo dejas afuera, índice es el valor predeterminado para los nombres de funciones omitidos, y el valor predeterminado para el método HTTP es alguna. Por supuesto, alguna no es un método HTTP real, pero es un valor que Slim utiliza para coincidir con todos los tipos de métodos HTTP.

los addRoutes la función comienza con una para cada Bucle que recorre las rutas. A continuación, establecemos el método HTTP predeterminado, opcionalmente reemplazándolo con el método proporcionado si el @ El símbolo está presente. Luego, pasamos el resto de la ruta a una función para recuperar una devolución de llamada y la adjuntamos a una ruta. Finalmente, agregamos la ruta a la matriz..

Ahora veamos el processCallback () función:

función protegida processCallback ($ path) $ class = "Main"; if (strpos ($ path, ":")! == false) list ($ class, $ path) = explode (":", $ path);  $ function = ($ path! = "")? $ ruta: "índice"; $ func = function () usa ($ class, $ function) $ class = '\ Controllers \\'. $ clase; $ class = new $ class (); $ args = func_get_args (); devolver call_user_func_array (array ($ class, $ function), $ args); ; devuelve $ func; 

El segundo tema proviene de la arquitectura de Slim; está destinado a ser definido todo en un solo archivo.

Primero establecemos la clase por defecto a Principal, y anule esa clase si se encuentra el símbolo de dos puntos. A continuación, determinamos si una función está definida y usamos el método predeterminado índice si necesario. Luego pasamos los nombres de clase y función a un cierre y lo devolvemos a la ruta.

Dentro del cierre, vamos a añadir el nombre de la clase con el espacio de nombres. Luego creamos una nueva instancia de la clase especificada y recuperamos la lista de argumentos pasados ​​a esta función. Si recuerdas, mientras Slim verifica si una ruta coincide, construye lentamente una lista de parámetros basados ​​en comodines de la ruta. Esta función (func_get_args ()) se puede utilizar para obtener los parámetros pasados ​​en una matriz. Luego, usando el call_user_func_array () El método nos permite especificar la clase y la función, mientras pasamos los parámetros al controlador.

No es una función muy complicada una vez que la entiendes, pero es un muy buen ejemplo de cuándo los cierres son útiles..

Para recapitular, agregamos una función a nuestra Enrutador que le permite pasar una matriz asociativa que contiene rutas y rutas que se asignan a las clases y funciones. El último paso es procesar las rutas y ejecutar cualquiera que coincida. Siguiendo con la convención de nomenclatura Slim, llamémoslo correr:

función pública run () $ display404 = true; $ uri = $ this-> request-> getResourceUri (); $ method = $ this-> request-> getMethod (); foreach ($ this-> rutas como $ i => $ route) if ($ route-> matches ($ uri)) if ($ route-> supportHttpMethod ($ method) || $ route-> supportHttpMethod ("ANY ")) call_user_func_array ($ route-> getCallable (), array_values ​​($ route-> getParams ())); $ display404 = falso;  if ($ display404) echo "404 - ruta no encontrada"; 

Comenzamos por configurar el display404 variable, que representa ninguna ruta encontrada, a cierto. Si encontramos una ruta coincidente, estableceremos esto en falso y omita el mensaje de error. A continuación, utilizamos el objeto de solicitud de Slim para recuperar el método URI y HTTP actual.

Usaremos esta información para recorrer y encontrar coincidencias de nuestra matriz.

Una vez que el objeto de la ruta partidos() La función se ejecuta, usted puede llamar getParams () para recuperar los parámetros analizados. Usando esa función y la getCallable () Método, somos capaces de ejecutar el cierre y pasar los parámetros necesarios. Finalmente, mostramos un mensaje 404 si ninguna ruta coincide con el URI actual.

Vamos a crear la clase de controlador que contiene las devoluciones de llamada para estas rutas. Si ha estado siguiendo esto, entonces se habrá dado cuenta de que nunca forzamos un protocolo o tipo de clase. Si no desea crear una clase de controlador, entonces cualquier clase funcionará bien.

Entonces, ¿por qué crear una clase de controlador? La respuesta corta es que todavía no hemos usado Slim! Usamos partes de Slim para la solicitud HTTP y las rutas, pero el objetivo de esto era tener acceso fácil a todas las propiedades de Slim. Nuestra clase de controlador ampliará la clase Slim real, obteniendo acceso a todos los métodos de Slim.

Puede omitir esta y subclase de Slim directamente desde sus controladores..


Construyendo el Controlador

Básicamente, este controlador te permite modificar Slim mientras aún lo mantienes de vainilla. Nombra el archivo Controller.php, y escribe el siguiente código:

data = $ settings ['model'];  parent :: __ construct ($ settings); 

Cuando inicializa Slim, puede pasar a una variedad de configuraciones, desde el modo de depuración de la aplicación hasta el motor de plantillas. En lugar de codificar los valores en el constructor, los cargo desde un archivo llamado settings.php y pasar esa matriz en el constructor de los padres.

Debido a que estamos extendiendo Slim, pensé que sería genial agregar una configuración de "modelo", permitiendo a las personas conectar su objeto de datos directamente al controlador.

Esa es la sección que puede ver en medio del código anterior. Verificamos si el modelo Se ha establecido el ajuste y asignarlo al controlador. datos propiedad si es necesario.

Ahora crea un archivo llamado settings.php en la raíz de su proyecto (la carpeta con el compositor.json archivo), e ingrese lo siguiente:

 new \ Slim \ Extras \ Views \ Twig (), 'templates.path' => '… / Views', 'model' => array (objeto) ("mensaje" => "Hola mundo")); devuelve $ settings;

Estas son configuraciones Slim estándar con la excepción del modelo. Cualquiera que sea el valor asignado a la modelo la propiedad se pasa a la datos variable; esto podría ser una matriz, otra clase, una cadena, etc ... Lo establezco en un objeto porque me gusta usar el -> notación en lugar de la notación de paréntesis (matriz).

Ahora podemos probar el sistema. Si te acuerdas en el Enrutador clase, precedimos el nombre de la clase con el "Controlador"espacio de nombres. Abrir compositor.json agregue lo siguiente directamente después de la definición de psr-0 para el Nettuts espacio de nombres:

"name": "nettuts / slim_advanced", "require": "slim / slim": "2.2.0", "slim / extras": "*", "twig / twig": "*", " autoload ": " psr-0 ": " Nettuts ":" vendor / "," Controller ":" ./ "

Entonces, como antes, simplemente descargue el autocargador:

compositor dump-autoload

Si simplemente establecemos la ruta base al directorio raíz, entonces el espacio de nombres Controlador se asignará a una carpeta llamada "Controlador"en la raíz de nuestra aplicación. Así que crea esa carpeta:

mkdir controlador

Dentro de esta carpeta, crea un nuevo archivo llamado Main.php. Dentro del archivo, debemos declarar el espacio de nombres y crear una clase que amplíe nuestro Controlador clase base

datos-> mensaje;  prueba de función pública () echo "Página de prueba"; 

Esto no es complicado, pero vamos a tomarlo con moderación. En esta clase, definimos dos funciones; sus nombres no importan porque los mapearemos a las rutas más adelante. Es importante notar que accedo directamente a las propiedades desde el controlador (es decir, el modelo) en la primera función, y de hecho, tendrá acceso completo a todos los comandos de Slim.

Ahora vamos a crear el archivo público real. Cree un nuevo directorio en la raíz de su proyecto y asígnele un nombre público. Como su nombre lo indica, esto es donde residirán todas las cosas públicas. Dentro de esta carpeta, crea un archivo llamado index.php y escriba lo siguiente:

 'Main: index @ get', '/ test' => 'Main: test @ get'); $ router-> addRoutes ($ rutas); $ router-> run ();

Incluimos la biblioteca de carga automática de Composer y creamos una nueva instancia de nuestro enrutador. Luego definimos dos rutas, las agregamos al objeto enrutador y lo ejecutamos..

También debe activar mod_rewrite en Apache (o su equivalente utilizando un servidor web diferente). Para configurar esto, crea un archivo llamado .htaccess dentro de público Directorio y llenarlo con lo siguiente:

RewriteEngine On RewriteCond% REQUEST_FILENAME! -F RewriteRule ^ index.php [QSA, L]

Ahora todas las solicitudes a esta carpeta (que no coinciden con un archivo real) se transferirán a index.php.

En su navegador, navegue a su público directorio, y debería ver una página que dice "Hola mundo". Navegar a "/prueba", y debería ver el mensaje" Página de prueba ". No es muy emocionante, pero hemos movido con éxito todo el código lógico a controladores individuales.


Segundo round

Slim no es CodeIgniter, no es Symfony y no es Laravel.

Así que tenemos una funcionalidad básica, pero hay algunos bordes ásperos. Empecemos por el router..

A partir de este momento, mostramos un simple mensaje de error si no existe una ruta. En una aplicación real, queremos la misma funcionalidad que cargar una página normal. Queremos aprovechar la capacidad de Slim para cargar vistas, así como configurar el código de error de la respuesta.

Agreguemos una nueva variable de clase que contenga una ruta opcional (al igual que las otras rutas). En la parte superior del archivo, agregue la siguiente línea directamente después de la definición del objeto de solicitud:

protegido $ errorHandler;

A continuación, vamos a crear una función que acepte una ruta y le asigne una función de devolución de llamada. Esto es relativamente simple porque ya hemos abstraído esta funcionalidad:

función pública set404Handler ($ path) $ this-> errorHandler = $ this-> processCallback ($ path); 

Ahora vamos a ajustar el correr comando para ejecutar opcionalmente la devolución de llamada en lugar de simplemente mostrar el mensaje de error:

if ($ display404) if (es_callable ($ this-> errorHandler)) call_user_func ($ this-> errorHandler);  else echo "404 - ruta no encontrada"; 

Abra la clase de controlador. Aquí es donde puede ajustar la funcionalidad de Slim a sus preferencias personales. Por ejemplo, me gustaría la opción de omitir la extensión de archivo al cargar vistas. Así que en lugar de escribir $ this-> render ("home.php");, Yo solo quiero escribir: $ this-> render ("home");. Para hacer esto, vamos a anular el método de render:

función pública render ($ name, $ data = array (), $ status = null) if (strpos ($ name, ".php") === false) $ name = $ name. ".php";  parent :: render ($ nombre, $ datos, $ estado); 

Aceptamos los mismos parámetros que la función principal, pero verificamos si se proporciona la extensión de archivo y la agregamos si es necesario. Después de esta modificación, pasamos el archivo al método principal para su procesamiento..

Este es solo un ejemplo, pero deberíamos poner cualquier otro cambio aquí en el hacer() método. Por ejemplo, si carga las mismas páginas de encabezado y pie de página en todos sus documentos, puede agregar una función renderPage (). Esta función cargaría la vista pasada entre las llamadas para cargar el encabezado regular y el pie de página..

A continuación, echemos un vistazo a la carga de algunas vistas. En la raíz de tu proyecto crea una carpeta llamada "Puntos de vista"(la ubicación y el nombre se pueden ajustar en la settings.php expediente). Vamos a crear dos vistas nombradas prueba.php y error.php.

Dentro prueba.php, agregue lo siguiente:

título

Esta es la página nombre!

Y dentro de la error.php archivo, ingrese esto:

404

La ruta que estabas buscando no se pudo encontrar.

Además, modifica la Principal controlador cambiando el índice() función a lo siguiente:

índice de función pública () $ this-> render ("test", array ("title" => $ this-> data-> message, "name" => "Home")); 

Aquí, representamos la vista de prueba que acabamos de realizar y pasamos los datos para mostrar. A continuación, probemos una ruta con parámetros. Cambiar el prueba() función a lo siguiente:

prueba de función pública ($ título) $ this-> render ("prueba", array ("título" => $ título, "nombre" => "Prueba")); 

Aquí, vamos un paso más allá al recuperar el título de la página desde la propia URI. Por último, pero no menos importante, agreguemos una función para la página 404:

función pública notFound () $ this-> render ('error', array (), 404); 

Usamos el hacer() El tercer parámetro opcional de la función, que establece el código de estado HTTP de la respuesta..

Nuestra edición final está en index.php Para incorporar nuestras nuevas rutas:

$ route = array ('/' => ", '/ test /: title' => 'Main: test @ get'); $ router-> addRoutes ($ route); $ router-> set404Handler (" Main: notFound "); $ router-> run ();

Ahora debería poder navegar a las tres rutas y ver sus vistas respectivas.


Conclusión

Con todo lo que hemos logrado, seguro que tiene algunas preguntas sobre por qué Slim no ofrece estas modificaciones. Parecen lógicos, no se alejan demasiado de la implementación de Slim y tienen mucho sentido. Josh Lockhart (el creador de Slim) lo expresó mejor:

"Slim no es CodeIgniter, no es Symfony y no es Laravel. Slim es Slim. Fue diseñado para ser liviano y divertido, a la vez que puede resolver el 80% de los problemas más comunes. En lugar de preocuparse por el borde En los casos, se enfoca en ser simple y tener un código base fácil de leer ".

A veces, como desarrolladores, estamos tan atrapados cubriendo escenarios locos que nos olvidamos de lo que es realmente importante: el código. Mods, como el que se encuentra en este tutorial, solo son posibles debido a la simplicidad y verbosidad del código. Entonces, sí, puede haber algunos casos de vanguardia que requieran atención especial, pero se obtiene una comunidad activa, lo que, en mi opinión, supera ampliamente los costos..

Espero que hayan disfrutado este artículo. Si tiene alguna pregunta o comentario, deje un mensaje abajo. También puede contactarme a través del canal IRC en Freenode en el #nettuts canal.