Crear Router PHP

Creo que tus habilidades PHP ahora han madurado hasta el punto de que está listo para construir un enrutador PHP personalizado desde cero. Esto nos dará la oportunidad de discutir la organización del código, los códigos de respuesta y más.

Que aprenderemos

  • Routers
  • Status Code
  • Code Organization

Carpeta especifica


Crear una carpeta especifica para los controladores

controllers/ 

Luego crear un archivo único index.php que se encargara de administrar las rutas, en index.php:

<?php 
require 'functions.php';
$uri = $_SERVER['REQUEST_URI'];

if ($uri === '/') {
    require 'controllers/index.php';
} else if ($uri === '/about') {
    require 'controllers/about.php';
} else if ($uri === '/contact') {
    require 'controllers/contact.php';
} 

Podemos ver en la variable $_SERVER['REQUEST_URI'] que ahi es donde vemos el url que el usuario dio, pero que pasa si el usuario pasa un url invalido o que no existe en nuestros controladores, por ejemplo: http://localhost:8888/about?name:enrique vemos parámetros de key:value pair, que la podemos ver si la imprimimos con:

$uri = $_SERVER['REQUEST_URI'];
dd($uri);

lo cual nos da string(19) "/about?name:enrique" .

Parse URI


Entonces como podemos eliminar el resto de caracteres que no nos interesa, para eso podemos usar una función de PHP.

$uri = $_SERVER['REQUEST_URI'];
 
// $uri2 = parse_url($uri);
// dd($uri2);

// También la podemos escribir asi
dd(parse_url($uri));

lo cual nos da como resultado

array(2) {
  ["path"]=>
  string(6) "/about"
  ["query"]=>
  string(12) "name:enrique"
}

Entonces ya podemos usar:

<?php 
require 'functions.php';

$uri = parse_url($_SERVER['REQUEST_URI'])['path'];

if ($uri === '/') {
    require 'controllers/index.php';
} else if ($uri === '/about') {
    require 'controllers/about.php';
} else if ($uri === '/contact') {
    require 'controllers/contact.php';
}

Con esto ya nos aseguramos que aun que en la URL venga como http://localhost:8888/about?name:enrique el program nos sigue redirigiendo correctamente a la pagina /about conservando n su forma correcta los parámetros en la URL.

Simplificar con un array asociativo


Para simplificar un poco mas las condicionales de los if, podemos usar un array key:value para almacenar las rutas y usar otra función de PHP array_key_exists($key,$array);, entonces el código queda así:

<?php 
require 'functions.php';
$uri = parse_url($_SERVER['REQUEST_URI'])['path'];
$routes = [
    '/' => 'controllers/index.php',
    '/about' => 'controllers/about.php',
    '/contact' => 'controllers/contact.php'
];
if ( array_key_exists($uri, $routes) ) {
    require $routes[$uri];
} else {
    http_response_code(404);
    echo "Pagina no encontrada!";
    die();
}

Lo podemos mejorar aun mas si hacemos una re-dirección a una pagina que se encargue de desplegar el mensaje de pagina no encontrada! En views/404.php

<?php require('partials/head.php') ?>
<?php require('partials/nav.php') ?>

<main>
    <div class="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
        <h1 class="text-2xl font-bold">Pagina NO encontrada!</h1>
        <p class="mt-4">
            <a href="/" class="text-blue underline">Regresar a inicio.</a>
        </p>   
    </div>
</main>

<?php require('partials/footer.php') ?>

Y en index.php

if ( array_key_exists($uri, $routes) ) {
    require $routes[$uri];
} else {
    http_response_code(404);
    require 'views/404.php';
    die();
}

Todo funciona bien hasta aquí! Todavía podemos refactor un poco el código, en index.php

<?php 
require 'functions.php';

$uri = parse_url($_SERVER['REQUEST_URI'])['path'];
$routes = [
    '/' => 'controllers/index.php',
    '/about' => 'controllers/about.php',
    '/contact' => 'controllers/contact.php'
];

// función para display 404 page 
function abort($code = 404){
    http_response_code($code);
    require "views/{$code}.php";
    die();
}

if ( array_key_exists($uri, $routes) ) {
    require $routes[$uri];
} else {
    abort();
}

Y por ultimo podemos pasar la función a las funciones generales del sistema y el array_key_exists($uri, $routes) pasarlo su propia función

<?php 
require 'functions.php';

$uri = parse_url($_SERVER['REQUEST_URI'])['path'];
$routes = [
    '/' => 'controllers/index.php',
    '/about' => 'controllers/about.php',
    '/contact' => 'controllers/contact.php'
];

// función para route to controller
function routeToController($uri, $routes){
    if ( array_key_exists($uri, $routes) ) {
        require $routes[$uri];
    } else {
        abort();
    }
}

// función para display 404 page 
function abort($code = 404){
    http_response_code($code);
    require "views/{$code}.php";
    die();
}

routeToController($uri, $routes);

Por ultimo podemos pasar casi todo el código a su propio archivo router.php

<?php 

$uri = parse_url($_SERVER['REQUEST_URI'])['path'];

$routes = [
    '/' => 'controllers/index.php',
    '/about' => 'controllers/about.php',
    '/contact' => 'controllers/contact.php'
];

// función para route to controller
function routeToController($uri, $routes){
    if ( array_key_exists($uri, $routes) ) {
        require $routes[$uri];
    } else {
        abort();
    }
}

// función para display 404 page 
function abort($code = 404){
    http_response_code($code);
    require "views/{$code}.php";
    die();
}

routeToController($uri, $routes);

Con esto tenemos un archivo dedicado exclusivamente para manejar las rutas. Y el index.php nos queda asi:

<?php 

require 'functions.php';
require 'router.php';

Listo!
Continuar …