web

Rutas en PHP con Phroute

Introducción

Los frameworks como Laravel traen incluido un router, algo que maneja y parsea las rutas por nosotros. Se encarga, por ejemplo, de saber si el verbo HTTP es GET, POST, PUT, etcétera. Y llama a una función de un controlador dependiendo de lo que digamos.

Parsear las rutas “a mano” es un poco complicado, pero aquí nos encontramos con un problema… ¿Qué hacemos si sólo queremos las rutas, pero no un framework completo? es decir, necesitamos algo ligero que sólo se encargue de las rutas, pero que al mismo tiempo sea poderoso e inteligente, además de rápido.

Phroute viene a solucionar este problema. Es una simple librería PHP que se encargará de todo el trabajo pesado para que nosotros sólo nos concentremos en cuáles métodos se llamarán dependiendo de cada petición o acción.

Consiguiendo librería

Usando composer

Nota: recomiendo instalarlo de esta forma. Si no sabes instalar Composer aquí dejo un tutorial. También recomiendo este post.

Para este ejemplo voy a crear un nuevo proyecto llamado “ejemplo_rutas” en mi carpeta pública y voy a iniciar el proyecto con composer init:

Como vemos, composer nos pregunta el nombre del paquete, la licencia, etcétera. Podemos establecerlos o presionar Enter para aceptar los que vienen por defecto. Después, composer nos preguntará si deseamos definir las dependencias, a lo que aceptaremos presionando Enter:

Pequeño aviso: si tú ya tienes un proyecto existente con Composer simplemente ejecuta composer require phroute/phroute

En donde dice search for a package buscaré phroute y presionaré Enter, para que aparezcan los siguientes resultados:

Ahora nos dice que ingresemos el número del paquete, en este caso es el 0 (el que dice phroute/phroute, puede cambiar el número pero no el nombre). Entonces escribo 0 y en la siguiente línea en donde pregunta la versión,, no escribo nada y simplemente presiono Enter:

El asistente me indica que, como no puse una mínima versión se va a pedir la versión 2.1 o superior; cosa que ahora no importa.

Ahora me dice que busque otro paquete, pero ya no necesito otro, así que presiono Enter sin escribir nada. Cuando haya hecho eso, ahora me preguntará si deseo definir mis dependencias de desarrollo, es decir, aquellas que no serán usadas en producción.

Escribo que no, porque no deseo hacerlo:

Finalmente me pide una confirmación, a la que acepto.

Ahora, como paso final, tengo que instalar las dependencias definidas en el archivo composer.json. Porque el asistente sólo las define, pero nosotros tenemos que instalarlas. Así que usamos:

composer install

 

Usando librería

Si abrimos el proyecto de ejemplo, veremos lo siguiente:

En la raíz vamos a crear un archivo llamado rutas.php que será el punto de entrada a nuestra aplicación. En ella cargaremos todas las clases necesarias con el autoload de composer, definiremos nuestras rutas y despacharemos los resultados, también manejaremos las excepciones si no se encuentra alguna ruta o método.

Pero antes, una breve explicación

Phroute necesita un poco de nuestra ayuda

La librería parseará la ruta y los verbos, pero para ello nosotros tenemos que mandarle la ruta “limpia”. Por ejemplo, la ruta normal, en mi caso, es /ejemplo_rutas/rutas.php pero a la librería sólo le importa lo que está después de “rutas.php” que en este caso sería una cadena vacía.

Si no se entiende, ahora supongamos la siguiente ruta para, digamos, obtener todos los usuarios: /ejemplo_rutas/rutas.php/usuarios

Phroute necesita sólo “usuarios” y nada más, no necesita lo demás.

Por eso tenemos que mandarle esa cadena limpia, además de pasarle el método de $_SERVER[‘REQUEST_METHOD’].

No pasa nada, no es cosa del otro mundo y de hecho alguien más ya lo ha hecho por nosotros. Hago esta explicación para entender la función processInput que está presente en el código.

Ahora sí, veamos el código:

Código

<?php
require __DIR__ . '/vendor/autoload.php'; #Cargar todas las dependencias
use Phroute\Phroute\RouteCollector;
use Phroute\Phroute\Dispatcher;
use Phroute\Phroute\Exception\HttpRouteNotFoundException;
use Phroute\Phroute\Exception\HttpMethodNotAllowedException;

$collector = new RouteCollector();

$collector->get("/", function(){
 return "Esta es la raíz";
});


$despachador = new Dispatcher($collector->getData());
$rutaCompleta = $_SERVER["REQUEST_URI"];
$metodo = $_SERVER['REQUEST_METHOD'];
$rutaLimpia = processInput($rutaCompleta);

try {
    echo $despachador->dispatch($metodo, $rutaLimpia); # Mandar sólo el método y la ruta limpia
} catch (HttpRouteNotFoundException $e) {
    echo "Error: Ruta no encontrada";
} catch (HttpMethodNotAllowedException $e) {
    echo "Error: Ruta encontrada pero método no permitido";
}


/**
 * Gracias a https://www.sitepoint.com/fast-php-routing-phroute/
 */function processInput($uri)
{
    return implode('/',
        array_slice(
            explode('/', $uri), 3));
}
?>

Como podemos ver en la línea 10 definimos lo que responderá a una petición GET en la raíz. Si en mi navegador voy al archivo, es decir, a la raíz de las rutas, veré esto:

Pero bueno, eso lo podemos hacer con cualquier cosa, y no se nota la diferencia. Agreguemos entonces más métodos para ver lo que sucede.

<?php
require __DIR__ . '/vendor/autoload.php'; #Cargar todas las dependencias
use Phroute\Phroute\RouteCollector;
use Phroute\Phroute\Dispatcher;
use Phroute\Phroute\Exception\HttpRouteNotFoundException;
use Phroute\Phroute\Exception\HttpMethodNotAllowedException;

$collector = new RouteCollector();

$collector->get("/", function(){
 return "Esta es la raíz";
});

$collector->get("/usuarios", function(){
 return "Obtener los usuarios";
});

$collector->get("/usuario/{id}", function($id){
 return "Obtener el usuario con el id $id";
});

$collector->get("/ventas/{dia}/{mes}/{anio}", function($dia, $mes, $anio){
 return "Obtener las ventas del día $dia, mes $mes y año $anio";
});

$collector->get("/ventas/eliminadas/{dia}/{mes}/{anio}/", function($dia, $mes, $anio){
 return "Obtener las ventas ELIMINADAS del día $dia, mes $mes y año $anio";
});

$collector->get("/esta/es/una/ruta/larga/{valor1}/bla/{otro_valor}/bla", function($valor1, $valor2){
 return "Ruta muy larga, valor1 es $valor1 y valor 2 es $valor2";
});


$despachador = new Dispatcher($collector->getData());
$rutaCompleta = $_SERVER["REQUEST_URI"];
$metodo = $_SERVER['REQUEST_METHOD'];
$rutaLimpia = processInput($rutaCompleta);

try {
    echo $despachador->dispatch($metodo, $rutaLimpia); # Mandar sólo el método y la ruta limpia
} catch (HttpRouteNotFoundException $e) {
    echo "Error: Ruta no encontrada";
} catch (HttpMethodNotAllowedException $e) {
    echo "Error: Ruta encontrada pero método no permitido";
}


/**
 * Gracias a https://www.sitepoint.com/fast-php-routing-phroute/
 */function processInput($uri)
{
    return implode('/',
        array_slice(
            explode('/', $uri), 3));
}
?>

Ahora sí tenemos algunas rutas. Pongamos atención en las llaves {}, son variables que serán pasadas (en el mismo orden) a la función anónima. Pueden ser números o cadenas. Al visitar cada una de esas rutas, obtengo lo siguiente:

Obviamente no siempre vamos a regresar cadenas, de hecho podemos regresar lo que sea, instanciar clases, regresar objetos, etcétera.

Más allá de las peticiones GET

Como lo dije al inicio, podemos manejar los métodos más comunes de HTTP. Por ejemplo, ya vimos los que sirven para “obtener” datos. Y para, por ejemplo, agregar un nuevo usuario (se utiliza POST regularmente) podemos llamar a /usuario así:

<?php
$collector->post("/usuario", function(){
 $usuario = file_get_contents("php://input"); //Si es por AJAX
 $usuario = $_POST["usuario"]; //Si es por un formulario, cosa no recomendable
 //Aquí agregar la magia :)
});
?>

Podemos llamar también a PUT y DELETE utilizando ->put y ->delete respectivamente. Y claro que podemos acceder a los valores de la URL. Por ejemplo, para editar un usuario dependiendo de su id usando PUT se me ocurre lo siguiente:

<?php
$collector->put("/usuario/{id}", function($id){
 $usuario = file_get_contents("php://input"); //Si es por AJAX
 $usuario = $_POST["usuario"]; //Si es por un formulario, cosa no recomendable
 echo "Editamos el usuario con el id $id";
 //Aquí agregar la magia :)
});
?>

Manejando excepciones

En el código que puse más arriba, encerramos al despachador en un try/catch. Esto es para manejar las excepciones que Phroute puede arrojar, las cuales son que no encuentre la ruta o que encuentre la ruta pero no corresponda al método.

Podemos manejarlas como se nos dé la gana.

Codificando

Algo que me gustó de esta librería es que llama a los métodos y hace un echo de lo que regresen, en lugar de simplemente llamarlos.

Es decir, si por ejemplo queremos construir una API que hable JSON, podemos hacer un json_encode sólo en el despachador, y a los demás métodos dejarlos intactos.

En ese caso, el despachador cambiaría a lo siguiente:

<?php
try {
    echo json_encode($despachador->dispatch($metodo, $rutaLimpia)); # Notar el json_encode
} catch (HttpRouteNotFoundException $e) {
    echo "Error: Ruta no encontrada";
} catch (HttpMethodNotAllowedException $e) {
    echo "Error: Ruta encontrada pero método no permitido";
}
?>

Y si, por ejemplo, deseamos responder con un arreglo a un método, podemos hacer esto:

<?php
$collector->get("/arreglo", function(){
 return ["Hola", "Mundo", "Soy", "Un", "Arreglo"];
});
?>

Conclusión

Esta librería permite esto y muchas cosas más, dejo la invitación a leer la documentación oficial en el repositorio de GitHub.

Me parece que esta librería puede funcionar para todo tipo de proyectos, y su configuración es realmente sencilla.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

1 semana hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.