URLs amigables en CodeIgniter: Controlador ‘catch-all’

Supongamos que estamos programando un CMS en CodeIgniter para integrarlo en nuestros proyectos, y queremos generar urls del estilo www.dominio.com/titulo-de-la-pagina. Una manera de enfocarlo sería por ejemplo crear un controlador Paginas con un método cargar, al que se le pasará como parámetro el título de la página en cuestión. De esta manera se llamaría a la url www.dominio.com/paginas/cargar/titulo-de-la-pagina. Para eliminar /paginas/cargar/ de la url editaríamos el fichero routing.php.

Esto supone un problema: si añadimos una regla genérica a las rutas para redirigir cualquier url al controlador paginas/cargar, no funcionaría el resto de controladores de nuestra aplicación. Deberíamos añadir una regla para cada página o bien para cada controlador que tengamos, con lo cual perdemos dinamismo y nos obliga a editar el fichero de rutas con cada cambio.

Extendiendo la librería Router

Lo que nos interesa es un controlador ‘catch-all’, que capture cualquier texto de la url y lo procese siempre que no exista otro controlador con ese nombre. Para ello vamos a extender la librería de rutas creando un archivo MY_Router.php y guardándolo en application/libraries/. El método estándar para extender librerías es crear un archivo con el mismo nombre añadiendo el prefijo MY_. Aquí tenéis el código de MY_Router.php:

<?php
class MY_Router extends CI_Router {
 
    function MY_Router()
    {
        parent::CI_Router();
    }
 
    function _validate_request($segments)
    {
        // Comprueba que el controlador no existe
        if (!file_exists(APPPATH.'controllers/'.$segments[0].EXT))
        {
            $segments = array("paginas", "cargar", $segments[0]);
 
        }
        return parent::_validate_request($segments);
    }
}
?>

Como veis lo que hacemos es extender el método _validate_request de la librería Router, que es el que procesa los segmentos de nuestra url. En él comprobamos si el nombre del primer segmento de la url corresponde a un controlador existente, si no es así añadimos al array de segmentos nuestro controlador y método paginas y cargar y llamamos al método _validate_request de la librería Router original pasándole estos parámetros.

Finalmente nos queda crear el controlador Paginas.php:

<?php
class Paginas extends Controller {
    function __construct(){
        	parent::Controller();
    }
 
    function cargar($title){
    	.....
    }
}
?>

Dentro de la función cargar, $title contendrá el título de nuestra url, es decir el primer segmento que le pasamos: www.dominio.com/titulo-de-la-pagina. En dicha función lo gestionaremos como nos interese, siendo lo más lógico utilizar ese título como identificador de nuestra página y buscarla en la base de datos para mostrarla.

Compartir:

31 comentarios para “URLs amigables en CodeIgniter: Controlador ‘catch-all’”

  1. carlos dice:

    Excelente artículo, y ahora un pregunta, si tengo dos o mas tipos de contenido? y necsite enrutar a mas acciones? servira hacer alguna modificación?

    Saludos y buen tuto 🙂

  2. David Rojas dice:

    @carlos No entiendo exáctamente tu pregunta. Tal como está, la extensión MY_Router lo que hace es capturar el primer segmento de la url del título y enviarlo al controlador/método que definas en el array $segments, pero esto sólo lo hace si no encuentra ningún controlador que hayas creado cuyo nombre coincida con el segmento de la url.

  3. carlos dice:

    Hola de nuevo, le estoy dando media vuelta para ver si consigo hacer lo que quiero, quizas sea más facil d elo que parece, soy medio nuevo en Codeigniter.

    Por ejemplo tengo ds controladores uno de paginas y otro de post y cada uno una accion o metodo que recibe la url como parametro.

    Algun truco para que las url que envio me sepa distinguir la olibreria si es post o pagina, y lo rutee al controlador correcto? No se si me dejo entender., por lo pronto l agrgue un segundo parametro a la url que es un post para que lo distinga, pero quiero usar solo la url limpia. Habra alguna solucion, o me quda hacerla a la vieja usansa con la bd y definiedo el tipo de contenido.

    Gracias por la ayuda 🙂

  4. David Rojas dice:

    @carlos Si sólo quieres tener una url limpia para todo el contenido (sin añadir /post/titulo-de-la-pagina), me temo que la solución es como dices definir un tipo de contenido y comprobarlo en la BD.

  5. Hola David, nuevamente Juan de Colombia… Tengo la siguente duda. Este codigo que pones funciona para direcciones de este estilo:

    http://www.gabasip.com/index.php/article/index/2

    (direccion real), en donde el 7 es el id de un registro de base de datos?. Es decir, empezando con ete codigo podria sustitur esto

    http://www.gabasip.com/index.php/article/index/2

    por esto:

    http://www.gabasip.com/index.php/article/index/servicios

    sin perder la navegacion?, muchas gracias

    PD. en este sitio de ejemplo use tu codigo y la validacion del formulario de contacto muchas gracias!

  6. David Rojas dice:

    @Juan Con esa estructura que propones realmente no necesitas utilizar esto, es más fácil. Supongo que tendrás un controlador article, con un método index. Sólo tienes que pasarle una variable, $nombre por ejemplo, a esa función index (la defines como function index($nombre). Tendrás que meter un campo en la base de datos que tendrá una variable nombre con el nombre de la página (en este ejemplo, ‘servicios’). Dentro de la función index entonces obtendrás la página que quieras mostrar a partir de esa variable nombre (en vez de buscarla por el id).

  7. Muchas gracias David, esa es la solucion mas practica, le estaba dando demasiadas vueltas al asunto…

  8. consulta si hago esto que pasara con la pagina de errores ? funcionaran en caso que se le ponga una url que no exista:

    mysitio.com/papapapapappaapapa

  9. David Rojas dice:

    @jose Eso lo controlas tú mismo. Dentro de la función cargar es donde realizas la búsqueda para ver si esa url existe, y si no puedes llamar a un show_404 para que te muestre la página de error.

  10. Nicho dice:

    Qué tal David, buen día:
    Nunca terminaré de agradecerte lo que me has ayudado con tu blog y esta ocasión quisiera saber si conoces como; y me pudieras orientar, para integrar el script de http://mibew.org/sp/ que es un buen webmessenger para atención en línea y que quisiera acoplar en los proyectos de CI. Agradecería infinitamente tus consejos, gracias.

  11. David Rojas dice:

    @Nicho me temo que no puedo ayudarte con esto, no he usado nunca ese paquete así que no sé indicarte.

  12. Nicho dice:

    Ok, gracias por tu respuesta, yo quisiera imaginar que es como integrar alún otros script como los de soporte o como integrar phpbb o wordpress…es que lo he intentado pero como lo instalo en una carpeta en el root y el CI también lo tengo en el root, pues a la hora de querer entrar a la administración del mibew no se puede puesto que no hay un controlador para ello, eso mismo me pasó una vez que intenté hacerlo con phpbb y lo rsolví metiendo el sitio en una carpeta, el foro en otra y un index en el root que me direccionaba al sitio echo con CI, pero lo que quisiera hacer es que todos estuvieran integrados en el CI sin necesidad de hacer lo anterior…eso es posible?

  13. David Rojas dice:

    @Nicho Si instalas algo como wordpress o phpbb lo mejor es que lo tengas separado en una carpeta cada cosa. Si configuras bien el .htaccess no debe darte problemas. Si lo que usas no es un sistema completa sino una clase en php o algo por el estilo, lo que puedes es hacerte tu propia librería en CI que ‘envuelva’ el componente que quieres instalar. Pero como te digo, eso es para clases o widgets concretos, no un sistema completo como wordpress.

  14. Nicho dice:

    Ok…ya entiendo, muchas gracias, entonces le buscaré con el .htaccess…no se gran cosas, pero le voy a buscar. Gracias nuevamente.

  15. Nicho dice:

    Esta es otra pregunta, aprovechando del tema de las urls. ¿Cómo es que haces para enlazar con el título, si no veo por ningún lado la id de la entrada? ¿a caso el truco está en agregar un campo a la tabla que contenga la url y se hace la búsqueda con él?¿o como?, lo que en mi caso he echo para hacer algo como lo tuyo es que en un segmento le pongo la id de la entrada y en otro el título, pero con agrado veo que el tuyo está “a todo dar” como decimos en méxico.

  16. David Rojas dice:

    @Nicho Efectivamente, tienes que añadir un campo a la tabla con el “slug” o segmento de la url, y hacer la búsqueda en él.

  17. galax13 dice:

    esto es un ejemplo de como trabajar mod_rewrite ???? hay que configurar alguna adisional en el archivo .htacces

  18. David Rojas dice:

    @galax13 No, mod_rewrite y htaccess es otra cosa, esto se maneja tan solo a través del routing de codeigniter

  19. Juan Pablo dice:

    hola david, tengo una duda, estoy probando hace rato hacer que mi sitio maneje urls como:
    http://www.mipagina.com/funcion/parametro
    en vez de:
    http://www.mipagina.com/index.php/controlador/funcion/parametro

    ya probé con el metodo de crear el archivo .htaccess pero no logro hacerlo funcionar.

    con este método que explicas acá se puede hacer practicamente lo mismo?

    si me das el afirmativo empiezo a probar

    muchas gracias 😉

  20. David Rojas dice:

    @Juan Pablo No, me temo que esto no tiene nada que ver, el index.php sólo puedes eliminarlo de la url con el .htaccess.

  21. Juan Pablo dice:

    ah ok, de paso te pregunto, no tendrás explicado por ahi bien como se hace eso no? porque segui los pasos de un par de tutoriales que se encuentran por ahi pero no me funcionó

    salu2

  22. Antonio dice:

    Hola David. En primer lugar felicitarte está página la cual se está convirtiendo en una guía de referencia para mi aprendizaje.

    Creo que este artículo me viene bien para un problema que pretendo solventar al utilizar CodeIgniter.Lo que pasa es que soy algo novato y aún me cuesta atar todos los cabos.
    Resulta que estoy creando una página (www.coches.com) donde los usuario se dan de alta y crean un perfil donde muestran sus coches. Así pues, me gustaría que para entrar en el perfil de cada usuario pudiera accederse intruduciendo la dirección web de la página seguido de el nombre del usuario (www.coches.com/antonio)
    Si no me equivoco, podría utilizar este tutorial para llevar a cabo lo que pretendo, no es así?

  23. David Rojas dice:

    @Antonio Sí, efectivamente este método es perfecto para lo que necesitas 🙂

  24. Con esto podria hacer tipo facebook que te da una url como perfil con tu nombre o nick…
    ????

  25. David Rojas dice:

    @Victor Manuel: Sí, esa es la idea.

  26. Skaler dice:

    Hola David,

    tengo una pregunta. Quizá es facil y no lo he visto.

    Estoy intentando hacer una aplicacion con URLs amigables. Te comento el caso:

    http://www.dominio.com/es/mi_pagina_de_contenido
    http://www.dominio.com/en/my_content_page

    Estas 2 URLs deben apuntar al controlador page.

    Esto es facil con el route de CI:
    $route[“es/mi_pagina_de_contenido”] = “page”;
    $route[“en/my_content_page”] = “page”;

    Y el problema esta que hace la redireccion y por lo tanto cambia la URL y queda http://www.dominio.com/page

    Y esto no me gusta ya que me perjudica a nivel de SEO y me gustaria que la URL no cambiara de la original.

    Se puede?

  27. David Rojas dice:

    @Skaler No debe hacerte una redirección, el routing de CI hace un mapeo de la url que le digas a otra url, pero no cambia la url original, el rerouting es interno a nivel de controladores y no efectuando redirecciones, mira a ver si tienes algún error. Ten en cuenta también que puedes usar comodines y expresiones regulares, échale un ojo a la documentación de CI.

  28. L@US87 dice:

    Hola David he checado todos tus videos y la verdad me han ayudado muchisimo, por lo que me gustaria agradercerte y por otro lado ver la posibilidad de hacer uno sobre como combinar filtros con paginacion, es decir opciones de busqueda, para cuando se deseen obtener datos con ciertos criterios y estos se puedan paginar.
    Te lo agradezco mucho!!!!

  29. David dice:

    Hola David, fantastica página que me està dando algo de luz con esto de los frameworks MVC etc. Cuesta mucho encontrar buenos blogs professionales como el tuyo.

    Estaba tratando de poner en funcionamiento este truco, quiero hacer lo siguiente:
    http://www.doinio.com/pagina/
    http://www.doinio.com/pagina1/pagina2/

    Cada entrada de la base de datos tiene en un campo el nombre del su controlador, si no encuentra ninguno el controlador serà el de paginas. Entiendo as o menos lo que quiero hacer y donde lo tengo que hacer, he visto tu otro tutorial y creo que sabré hacer la consulta y todo esto.

    Pero tengo un problema, he creado el MY_Router i el controlador Paginas, pero cuando pongo http://localhost/codeigniter/pagina no me sale nada, sale not found.
    Me falta algo? hay que poner algo en el htaccess? he puesto un $this->load->view(‘welcome_message’); dentro de la función cargar, igual que en el indice del controlador welcome que vien por defecto y nada.

    Perdona las molestias

  30. Yusniel dice:

    Muy buena la extensión de esta librería.

  31. La verdad que es ,uy interesante el artículo, me sirvio, pero le hice algunos cambios, yo estoy trabajando con la versión 2.5 de CI y al archivo MY_Router.php hay que alojarlo en la carpeta application/core y comentarle (o quitar) lo siguiente:
    function MY_Router()
    {
    parent::CI_Router();
    }

    Después todo lo demás anduvo joya, excelente man, muchas gracias.

Deja un comentario

Introduce tu información personal en el formulario, regístrate o identifícate usando twitter con el siguiente botón.

Time limit is exhausted. Please reload the CAPTCHA.

RSS iTunes podcast Twitter

Categorías

Enlaces

Archivos