16: Limpiando la URL en PHP (MVC)

Introducción

Hola alumnos, en este tutorial vamos a aprender a limpiar las URLs en nuestra aplicación PHP bajo el patrón Modelo Vista Controlador (MVC). Vamos a solucionar un problema muy común y a hacer nuestra aplicación más profesional y segura.

¿Qué problema tenemos?

Ya hemos creado una página interactiva bajo el patrón modelo vista controlador, pero vamos a encontrar un pequeño problema. El usuario puede navegar entre los contenidos y son contenidos modulares.

El usuario no sabe que son contenidos modulares, solamente lo sabe la persona que desarrolló la aplicación. A través de los módulos tenemos:

  • Módulo de navegación

  • Módulo de contáctenos

  • Módulo de nosotros

  • Módulo de servicios

Estos módulos pueden tener su propia hoja de estilo, pero la idea no es esa. La idea es que primero hayamos hecho toda la plantilla completa y luego la separamos por módulos y esa misma hoja de estilo nos sirva para todos.

El error que aparece

Cuando el usuario inicia la aplicación en el index.php, PHP se va a encontrar con un error. ¿Por qué? Porque todos los enlaces los estamos llamando a través de una variable GET llamada action.

text
http://localhost/cursoPHP/03.MVC/index.php?action=servicios

Pero para la página de inicio, la URL sería:

text
http://localhost/cursoPHP/03.MVC/index.php

En este caso, la variable action no existe en la URL, por lo tanto el sistema no sabe qué módulo cargar y nos mostrará un error.

Solución paso a paso

Paso 1: Modificar el controlador para verificar si existe la variable GET

Vamos a editar el archivo controller.php. Necesitamos verificar si la variable action viene con información o no.

php
<?php
// controller.php

class EnlacesPaginasController {
    
    public function enlacesPaginasController() {
        
        // Verificamos si la variable action viene con información
        if(isset($_GET["action"])) {
            // Si trae información, guardamos ese valor
            $enlacesController = $_GET["action"];
        } else {
            // Si NO trae información, asignamos "index"
            $enlacesController = "index";
        }
        
        // Llamamos al modelo y le pasamos el valor
        $respuesta = EnlacesPaginas::enlacesPaginasModel($enlacesController);
        
        // Incluimos la vista correspondiente
        include $respuesta;
    }
}
?>

Explicación del código:

  • isset($_GET["action"]) - Esta función verifica si la variable action existe y tiene contenido.

  • Si existe (true), guardamos su valor en $enlacesController.

  • Si no existe (false), asignamos el valor "index" a $enlacesController.

  • Luego enviamos este valor al modelo.

Paso 2: Modificar el modelo para manejar los enlaces

Ahora vamos a editar el archivo model.php para que reconozca el valor "index" y cargue la página de inicio.

php
<?php
// model.php

class EnlacesPaginas {
    
    public function enlacesPaginasModel($enlacesModel) {
        
        // Verificamos si el enlace está en nuestra lista blanca
        if($enlacesModel == "nosotros" || 
           $enlacesModel == "servicios" || 
           $enlacesModel == "contactenos") {
            
            $module = "views/modules/" . $enlacesModel . ".php";
            
        } else if($enlacesModel == "index") {
            // Si es index, cargamos la página de inicio
            $module = "views/modules/inicio.php";
        }
        
        return $module;
    }
}
?>

Explicación del código:

  • Si $enlacesModel es "nosotros", "servicios" o "contactenos", cargamos el módulo correspondiente.

  • Si $enlacesModel es "index", cargamos el módulo "inicio.php".

  • Devolvemos la ruta del módulo a incluir.

Paso 3: Probar la solución

Actualizamos nuestra aplicación y hacemos clic en "Inicio". Veremos que:

  • La URL está limpia: http://localhost/cursoPHP/03.MVC/index.php

  • La página carga correctamente el módulo inicio.php

¡Perfecto! Hemos solucionado el error.

Pero tenemos otro problema...

Ahora, ¿qué pasa si un usuario malintencionado que conoce PHP escribe en la URL?

text
http://localhost/cursoPHP/03.MVC/index.php?action=chicas

Como "chicas" no está en nuestra lista de enlaces válidos, PHP mostrará un error. Esto no es seguro ni profesional.

Solución: Crear una lista blanca

Una lista blanca (whitelist) es un conjunto de valores permitidos. Todo lo que no esté en esta lista será rechazado o redirigido.

Vamos a modificar nuestro modelo para incluir una lista blanca:

php
<?php
// model.php - Versión mejorada con lista blanca

class EnlacesPaginas {
    
    public function enlacesPaginasModel($enlacesModel) {
        
        // LISTA BLANCA: solo estos valores son permitidos
        if($enlacesModel == "nosotros" || 
           $enlacesModel == "servicios" || 
           $enlacesModel == "contactenos") {
            
            $module = "views/modules/" . $enlacesModel . ".php";
            
        } else if($enlacesModel == "index") {
            
            $module = "views/modules/inicio.php";
            
        } else {
            // Si NO está en la lista blanca, cargamos inicio
            $module = "views/modules/inicio.php";
        }
        
        return $module;
    }
}
?>

¿Qué hemos agregado?

  • Un else final que atrapa cualquier valor no permitido.

  • Si el usuario escribe cualquier palabra que no esté en nuestra lista blanca, automáticamente lo redirigimos a la página de inicio.

Probando la lista blanca

Ahora, si un usuario escribe:

text
http://localhost/cursoPHP/03.MVC/index.php?action=chicas

En lugar de un error, verá la página de inicio. También funciona con:

text
http://localhost/cursoPHP/03.MVC/index.php?action=malicious
text
http://localhost/cursoPHP/03.MVC/index.php?action=hack

Resumen de archivos finales

controller.php (Completo)

php
<?php
// controller.php

class EnlacesPaginasController {
    
    public function enlacesPaginasController() {
        
        // Verificamos si la variable GET existe
        if(isset($_GET["action"])) {
            $enlacesController = $_GET["action"];
        } else {
            $enlacesController = "index";
        }
        
        // Llamamos al modelo
        $respuesta = EnlacesPaginas::enlacesPaginasModel($enlacesController);
        
        // Incluimos la vista
        include $respuesta;
    }
}
?>

model.php (Completo)

php
<?php
// model.php

class EnlacesPaginas {
    
    public function enlacesPaginasModel($enlacesModel) {
        
        // LISTA BLANCA - Solo estos enlaces son permitidos
        if($enlacesModel == "nosotros" || 
           $enlacesModel == "servicios" || 
           $enlacesModel == "contactenos") {
            
            $module = "views/modules/" . $enlacesModel . ".php";
            
        } else if($enlacesModel == "index") {
            
            $module = "views/modules/inicio.php";
            
        } else {
            // Cualquier otro valor no permitido va a inicio
            $module = "views/modules/inicio.php";
        }
        
        return $module;
    }
}
?>

navegacion.php (Menú)

php
<!-- navegacion.php -->
<nav>
    <ul>
        <li><a href="index.php">Inicio</a></li>
        <li><a href="index.php?action=nosotros">Nosotros</a></li>
        <li><a href="index.php?action=servicios">Servicios</a></li>
        <li><a href="index.php?action=contactenos">Contáctenos</a></li>
    </ul>
</nav>

Conclusión

Hemos aprendido a:

  1. Limpiar la URL usando PHP para manejar cuando no existe la variable GET

  2. Crear una lista blanca para evitar que usuarios malintencionados inyecten valores no deseados

  3. Redirigir automáticamente los valores no permitidos a la página de inicio

Esto hace que nuestra aplicación sea:

  • Más profesional: URLs limpias y sin errores

  • Más segura: Evitamos inyección de parámetros no deseados

  • Más robusta: El sistema nunca muestra errores al usuario

En el próximo tutorial aprenderemos a optimizar aún más nuestro código y agregar nuevas funcionalidades.

URLs Amigables en PHP (URLs Limpias)

Introducción a URLs Amigables

Hola alumnos, en el tutorial anterior aprendimos a limpiar la URL solucionando el problema de la variable GET vacía y creando una lista blanca. Pero nuestras URLs todavía tienen ese formato con ?action= que no es muy bonito que digamos:

text
http://localhost/cursoPHP/03.MVC/index.php?action=nosotros
http://localhost/cursoPHP/03.MVC/index.php?action=servicios
http://localhost/cursoPHP/03.MVC/index.php?action=contactenos

¿No sería mucho mejor si pudiéramos tener URLs como estas?

text
http://localhost/cursoPHP/03.MVC/nosotros
http://localhost/cursoPHP/03.MVC/servicios
http://localhost/cursoPHP/03.MVC/contactenos
http://localhost/cursoPHP/03.MVC/

¡Eso se llaman URLs amigables (o friendly URLs)! Se ven más profesionales, son más fáciles de recordar y ayudan al posicionamiento en buscadores (SEO).

¿Cómo funcionan las URLs amigables?

Las URLs amigables funcionan mediante un archivo especial llamado .htaccess que reescribe las URLs. Este archivo le dice al servidor: "Cuando el usuario escriba una URL bonita, conviértela internamente a la URL con parámetros que nuestro sistema entiende".

Paso 1: Crear el archivo .htaccess

En la raíz de nuestro proyecto (carpeta 03.MVC), vamos a crear un archivo llamado .htaccess (con el punto al inicio):

text
# .htaccess
# Archivo para reescribir URLs y hacerlas amigables

# Activamos el motor de reescritura
RewriteEngine On

# Condición: No es un archivo real
RewriteCond %{REQUEST_FILENAME} !-f
# Condición: No es un directorio real
RewriteCond %{REQUEST_FILENAME} !-d

# Regla de reescritura
RewriteRule ^([a-zA-Z0-9_-]+)$ index.php?action=$1 [L]

Explicación del código línea por línea:

  • RewriteEngine On - Activa el motor de reescritura de URLs

  • RewriteCond %{REQUEST_FILENAME} !-f - Verifica que lo que pide el usuario NO sea un archivo real

  • RewriteCond %{REQUEST_FILENAME} !-d - Verifica que lo que pide el usuario NO sea un directorio real

  • RewriteRule ^([a-zA-Z0-9_-]+)$ index.php?action=$1 [L] - Toma cualquier palabra (letras, números, guiones) y la convierte en index.php?action=palabra

¿Qué hace exactamente este archivo?

Imaginemos que el usuario escribe en el navegador:

text
http://localhost/cursoPHP/03.MVC/nosotros

El archivo .htaccess intercepta esta petición y la transforma internamente en:

text
http://localhost/cursoPHP/03.MVC/index.php?action=nosotros

¡Pero el usuario sigue viendo la URL bonita! Es como un "traductor" entre lo que ve el usuario y lo que entiende nuestro sistema.

Paso 2: Actualizar nuestro menú (navegacion.php)

Ahora que tenemos URLs amigables, podemos actualizar nuestro menú para usar estas nuevas URLs:

php
<!-- navegacion.php -->
<nav>
    <ul>
        <li><a href="/cursoPHP/03.MVC/">Inicio</a></li>
        <li><a href="/cursoPHP/03.MVC/nosotros">Nosotros</a></li>
        <li><a href="/cursoPHP/03.MVC/servicios">Servicios</a></li>
        <li><a href="/cursoPHP/03.MVC/contactenos">Contáctenos</a></li>
    </ul>
</nav>

¿Ven la diferencia? Ya no usamos index.php?action=, sino directamente el nombre de la sección.

Paso 3: Verificar que nuestro controlador funcione

Lo bueno es que nuestro controlador y modelo ya están preparados para trabajar con estas URLs. Recordemos nuestro controller.php del tutorial anterior:

php
<?php
// controller.php
class EnlacesPaginasController {
    
    public function enlacesPaginasController() {
        
        // Verificamos si la variable action existe
        if(isset($_GET["action"])) {
            $enlacesController = $_GET["action"];  // Aquí llega "nosotros", "servicios", etc.
        } else {
            $enlacesController = "index";  // Si no hay action, va a index
        }
        
        // Llamamos al modelo
        $respuesta = EnlacesPaginas::enlacesPaginasModel($enlacesController);
        
        // Incluimos la vista
        include $respuesta;
    }
}
?>

El sistema sigue funcionando igual, porque internamente http://localhost/cursoPHP/03.MVC/nosotros se convierte en index.php?action=nosotros, y nuestro controlador recibe "nosotros" como antes.

Paso 4: Actualizar el modelo para manejar mejor las URLs

Podemos mejorar nuestro modelo para que funcione perfectamente con las URLs amigables:

php
<?php
// model.php
class EnlacesPaginas {
    
    public function enlacesPaginasModel($enlacesModel) {
        
        // Lista blanca con todos los módulos permitidos
        $modulosPermitidos = ["nosotros", "servicios", "contactenos", "inicio"];
        
        // Verificamos si el enlacesModel está en la lista blanca
        if(in_array($enlacesModel, $modulosPermitidos)) {
            
            // Si es "inicio", cargamos inicio.php
            if($enlacesModel == "inicio") {
                $module = "views/modules/inicio.php";
            } else {
                // Para los demás, cargamos el módulo correspondiente
                $module = "views/modules/" . $enlacesModel . ".php";
            }
            
        } else if($enlacesModel == "index") {
            // Si viene "index", cargamos inicio
            $module = "views/modules/inicio.php";
            
        } else {
            // Si no está en la lista blanca, cargamos inicio
            $module = "views/modules/inicio.php";
        }
        
        return $module;
    }
}
?>

¿Qué mejoramos?

  • Usamos in_array() para verificar si el valor está en la lista blanca

  • Hacemos el código más limpio y fácil de mantener

  • Si en el futuro agregamos más secciones, solo hay que agregarlas al array $modulosPermitidos

Comparativa: Antes vs Después

Antes (URLs feas):

text
http://localhost/cursoPHP/03.MVC/index.php?action=nosotros
http://localhost/cursoPHP/03.MVC/index.php?action=servicios
http://localhost/cursoPHP/03.MVC/index.php?action=contactenos
http://localhost/cursoPHP/03.MVC/index.php

Después (URLs amigables):

text
http://localhost/cursoPHP/03.MVC/nosotros
http://localhost/cursoPHP/03.MVC/servicios
http://localhost/cursoPHP/03.MVC/contactenos
http://localhost/cursoPHP/03.MVC/

Estructura final de nuestro proyecto

text
03.MVC/
├── .htaccess                 # Archivo para URLs amigables
├── index.php                 # Punto de entrada
├── controller.php
├── model.php
├── controllers/
│   └── controller.php
├── models/
│   └── model.php
└── views/
    ├── modules/
    │   ├── inicio.php
    │   ├── nosotros.php
    │   ├── servicios.php
    │   ├── contactenos.php
    │   └── navegacion.php
    └── template.php

Beneficios de las URLs amigables

1. Profesionalismo

php
// Antes (parece sistema amateur)
$url = "index.php?action=nosotros";

// Después (parece sitio profesional)
$url = "/nosotros";

2. SEO (Posicionamiento en buscadores)

Los buscadores como Google prefieren URLs limpias porque:

  • Son más fáciles de leer

  • Contienen palabras clave

  • Son más fáciles de recordar

3. Seguridad

Las URLs amigables ocultan que estamos usando parámetros GET, dificultando que usuarios malintencionados intenten inyectar código.

4. Facilidad de uso

html
<!-- Antes: URLs largas y feas -->
<a href="index.php?action=nosotros&lang=es&id=5">Nosotros</a>

<!-- Después: URLs limpias y bonitas -->
<a href="/nosotros">Nosotros</a>

Posibles problemas y soluciones

Problema 1: El archivo .htaccess no funciona

Solución: Verificar que Apache tenga activado el módulo mod_rewrite. En XAMPP/WAMP normalmente viene activado.

Problema 2: URLs relativas vs absolutas

php
<!-- Esto puede fallar con URLs amigables -->
<a href="nosotros">Nosotros</a> <!-- MAL -->

<!-- Esto funciona siempre -->
<a href="/cursoPHP/03.MVC/nosotros">Nosotros</a> <!-- BIEN -->
<a href="<?php echo $base_url; ?>/nosotros">Nosotros</a> <!-- MEJOR -->

Problema 3: Archivos CSS y JS que no cargan

Cuando usamos URLs amigables, las rutas relativas pueden confundirse. Solución: Usar siempre rutas absolutas desde la raíz.

html
<!-- En lugar de esto -->
<link rel="stylesheet" href="css/estilos.css">

<!-- Usar esto -->
<link rel="stylesheet" href="/cursoPHP/03.MVC/css/estilos.css">

Ejemplo completo funcionando

Cuando un usuario hace clic en "Nosotros":

  1. El navegador pidehttp://localhost/cursoPHP/03.MVC/nosotros

  2. .htaccess intercepta: "Esto no es un archivo real, lo convierto"

  3. Transformaciónhttp://localhost/cursoPHP/03.MVC/index.php?action=nosotros

  4. PHP procesa: Nuestro controlador recibe action=nosotros

  5. Modelo verifica: "nosotros" está en lista blanca

  6. Resultado: Se carga views/modules/nosotros.php

¡Y el usuario nunca se entera de esta magia!

Conclusión

Las URLs amigables son el estándar en el desarrollo web moderno. Con un simple archivo .htaccess transformamos nuestras URLs feas en URLs profesionales, mejorando la experiencia del usuario, el SEO y la seguridad de nuestra aplicación.

En el próximo tutorial aprenderemos a crear un sistema de rutas más avanzado que nos permita tener URLs aún más complejas como:

  • /nosotros/historia

  • /servicios/desarrollo-web

  • /contactenos/mensajes/15

¡Practiquen estos conceptos y verán cómo sus aplicaciones se ven mucho más profesionales!


Comentarios

Entradas más populares de este blog

token

¿Qué es un token y cómo se utiliza en una API?

Generación de Credenciales API