Paginación perfecta con PHP

baluart4 Enero 2008 - 1:55am 32 comentarios
Enviar por Email Imprimir

Desde hace meses atrás quería compartir con ustedes un código PHP de paginación de consultas MySQL, como renovando este viejo script. Pero acabo de leer un reciente artículo publicado en sitepoint y me ha fascinado, así que he decidido traducirlo, a mi manera por supuesto. Espero les agrade, ya que publico estos tutoriales después de tiempo. El artículo se titula en inglés Perfect PHP Pagination.

Paginación perfecta con PHP

La paginación es un tema que ha sido tratado hasta el cansancio. Docenas de artículos y documentos se pueden encontrar sobre ello; pero (y ustedes saben que hay un “pero”) no estamos completamente satisfechos con las soluciones que tenemos... Hasta ahora. En este artículo les mostraremos nuestra propuesta, una mejor alternativa.

Algunas clases de paginación requieren parámetros tales como acceso a una base de datos y una o dos cadenas SQL, que luego son pasadas al constructor. Las clases que utilizan este enfoque carecen de utilidad y flexibilidad, ¿Y si queremos cambiar el formato de los números en la parte superior o inferior, por ejemplo? ¿Habrá que modificar la función de salida, o la subclase de la clase principal, sólo para anular un método? Estas potenciales soluciones son muy restrictivas y no propician la reutilización del código.

Este tutorial es un intento para crear una clase más abstracta para el manejo de la paginación de resultados. Eliminando su dependencia a las conexiones a la BD y los strings SQL. El enfoque que trataremos le proveerá una mayor flexibilidad, lo que permitirá luego a cada desarrollador, darle el diseño propio a la paginación, bastando solamente la utilización de la clase a través del patrón de diseño orientado a objetos, conocido como Estrategia Patrón de diseño...

Desde hace meses atrás quería compartir con ustedes un código PHP de paginación de consultas MySQL, como renovando este viejo script. Pero acabo de leer un reciente artículo publicado en sitepoint y me ha fascinado, así que he decidido traducirlo, a mi manera por supuesto. Espero les agrade, ya que publico estos tutoriales después de tiempo. El artículo se titula en inglés Perfect PHP Pagination.

Paginación perfecta con PHP

La paginación es un tema que ha sido tratado hasta el cansancio. Docenas de artículos y documentos se pueden encontrar sobre ello; pero (y ustedes saben que hay un “pero”) no estamos completamente satisfechos con las soluciones que tenemos... Hasta ahora. En este artículo les mostraremos nuestra propuesta, una mejor alternativa.

Algunas clases de paginación requieren parámetros tales como acceso a una base de datos y una o dos cadenas SQL, que luego son pasadas al constructor. Las clases que utilizan este enfoque carecen de utilidad y flexibilidad, ¿Y si queremos cambiar el formato de los números en la parte superior o inferior, por ejemplo? ¿Habrá que modificar la función de salida, o la subclase de la clase principal, sólo para anular un método? Estas potenciales soluciones son muy restrictivas y no propician la reutilización del código.

Este tutorial es un intento para crear una clase más abstracta para el manejo de la paginación de resultados. Eliminando su dependencia a las conexiones a la BD y los strings SQL. El enfoque que trataremos le proveerá una mayor flexibilidad, lo que permitirá luego a cada desarrollador, darle el diseño propio a la paginación, bastando solamente la utilización de la clase a través del patrón de diseño orientado a objetos, conocido como Estrategia Patrón de diseño.

¿Qué es la Estrategia Patrón de diseño?

Imagina lo siguiente: Tú tienes en tu sitio un puñado de páginas web, por lo cual debes paginar las consultas. Tu sitio utiliza una función o una clase que se encarga de recuperar las consultas y de publicarlas con los enlaces de la paginación.

Esto está muy bien, hasta que decides cambiar el layout de los enlaces de la paginación en una o en todas las páginas de los resultados. Al hacerlo, lo más probable es que tengamos que cambiar el método al que le delegó esta responsabilidad.

Una mejor solución sería crear tantos layouts como desees, y elegir dinámicamente el que deseamos se muestre en tiempo de ejecución. La Estrategia Patrón de diseño nos permite hacer esto. En pocas palabras, la estrategia de diseño de patrones es un patrón de diseño, orientado a objetos, utilizados por una clase que quiere cambiar su comportamiento en tiempo de ejecución.

Utilizando las capacidades polimórficas de PHP, una clase contenedora de parámetros (como la que crearemos en este artículo) utiliza un objeto que implementa una interfaz y que define implementaciones concretas de los métodos que se definen en la interfaz.

Mientras que una interfaz no puede ser instanciada, la podemos referenciar implementando clases. Así, cuando nosotros creamos un nuevo layout, podemos dejar que la Estrategia o la Interfaz con el contenedor (la clase de paginación) instancien los layouts dinámicamente en tiempo de ejecución. Las llamadas que producen los enlaces paginados producirán una página acorde al layout instanciado.

Archivos requeridos

Como he mencionado, este tutorial no trata sobre los mecanismos de cómo los resultados son paginados, pero sí de cómo utilizar una interfaz para implementar esta lógica sin restringir su flexibilidad. Propongo como punto de partida una clase que contiene las funcionalidades para el registro de arrays primitivos u objetos - la clase de paginación -, así como una interfaz que todos los layouts de las páginas deben implementar (PageLayout) y una implementación para el diseño de una página (DoubleBarLayout). Además, todo el código que empleemos en el desarrollo de este artículo esta disponible para descargar.

Un ejemplo básico

Los siguientes ejemplos utilizan un array de strings. Aquí están los datos:

  • Andrew
  • Bernard
  • Castello
  • Dennis
  • Ernie
  • Frank
  • Greg
  • Henry
  • Isac
  • Jax
  • Kester
  • Leonard
  • Matthew
  • Nigel
  • Oscar

Sin embargo, este código puede ser fácilmente extendido para utilizarlo en un array de índices numéricos, caracteres u otros objetos que han sido asociados previamente de una base de datos.

Así es como utilizaremos la clase Paginated:

<?php
require_once "Paginated.php";
//create an array of names in alphabetic order
$names = array("Andrew", "Bernard", "Castello", "Dennis", "Ernie", Frank", Greg", "Henry", "Isac", "Jax", "Kester", "Leonard", "Matthew", "Nigel", "Oscar");
$pagedResults = new Paginated($names, 10, 1);
echo "<ul>";
while($row = $pagedResults->fetchPagedRow()) {
echo "<li>{$row}</li>";
}
echo "</ul>";
?>

Primero incluimos la clase Paginated e instanciamos un array con el constructor dando tres parámetros, dos de los cuales, los últimos, son opcionales.

  1. El primer parámetro es el array que mostraremos. Como hemos mencionado, estos datos pueden ser simples textos u objetos de los más complejos.
  2. El segundo parámetro es el número de resultados que queremos mostrar en una página. Por defecto se mostrarán 10 resultados.
  3. El tercer parámetro es el número de la página actual.

En el ejemplo anterior, hemos utilizado la constante 1 para especificar “Page 1”, sin embargo probablemente querrás pasar esto como un parámetro de la cadena de consulta (más detalles luego). Si una página inválida es dada al constructor, entonces se mostrará la página 1 por defecto.

Llamando al método fetchPagedRow desde dentro del bucle While, nuestro código itera a través del array, imprimiendo los primeros diez nombres de la lista (en el ejemplo, "Kester", "Leonard", "Matthew", "Nigel" y "Oscar" son omitidos). Estos nombres serán mostrados en la segunda página, pero como la imagen de abajo lo ilustra, no hay enlace a la página 2 aún. Aunque nuestra clase Paginated gestiona el acceso a cualquier objeto registrado por el programador, la responsabilidad de mostrar los enlaces de la paginación son delegados a una clase que implementa la interfaz PageLayout.

Paginación PHP

Vamos a añadir algo de código para mostrar los números de página, a continuación nos pondremos a profundizar un poco más en las funcionalidades de esta clase.

Crea un archivo nuevo de extensión PHP que contenga el siguiente código:

<?php
require_once "Paginated.php";
require_once "DoubleBarLayout.php";
//create an array of names in alphabetic order
$names = array("Andrew", "Bernard", "Castello", "Dennis", "Ernie", "Frank", "Greg", "Henry", "Isac", "Jax", "Kester", "Leonard", "Matthew", "Nigel", "Oscar");
$page = $_GET['page'];
$pagedResults = new Paginated($names, 10, 1);
echo "<ul>";
while($row = $pagedResults->fetchPagedRow()) {
echo "<li>{$row}</li>";
}
echo "</ul>";
$pagedResults->setLayout(new DoubleBarLayout());
echo $pagedResults->fetchPagedNavigation();
?>

Cuando miramos la secuencia de comandos ahora, veremos una lista con los diez primeros nombres, así como algunos datos adicionales de orientación que se muestran en la imagen siguiente. Nuestro script ahora muestra el texto “Page 1”, así como un enlace a la segunda página que dice "next >".

Paginación perfecta con PHP y MySQL

En el snippet anterior hemos hecho uso de una clase llamada DoubleBarLayout, que implemente la interfaz PageLayout y que contenga una implementación del método fetchPagedLinks. Este método toma dos parámetros que queremos sean añadidos a los enlaces (dado el caso).

Lo bueno de este método es que se aprovecha de las capacidades polimórficas de PHP, permitiendo la estrategia que este previamente registrado para ser llamado. Por lo tanto, es importante para nosotros, establecer la estrategia en primer lugar, antes de llamar al método. Establecer la estrategia se logra a través de llamada al método setLayout, el cual toma como parámetro un objeto que implemente la interfaz de PageLayout.

Coloca más de uno de estos enlaces y verá que el parámetro de la página y su valor de 2 está incluido en la URL del número de página. Sin embargo, en el estado actual, si se hace clic en el enlace a la segunda página, los nombres que esperamos no serán mostrados.

Esto es así, por el contructor de Paginated.

Este constructor toma 3 parametros.

  1. El array de variables primitivas u objetos que serán procesadas
  2. El número de registros que se mostrarán
  3. El número de página

Debido a que el método fetchPagedNavigation escribe un parámetro de consulta, nosotros podemos sustituir nuestro código duro de “1” por el valor de una variable $_GET['page']. De esta manera, si el usuario modifica el valor manualmente en la URL a algo que no sea válido, automáticamente Paginated colocará por defecto la página 1. ¿Cómo usted logra validar el parámetro GET? Es algo a gusto, por lo que no nos extenderemos en este punto.

Flexibilidad en los esquemas del diseño de páginas

La flexibilidad de esta clase se logra a través de la interfaz de PageLayout, la cual es parte del objeto Paginated. La interfaz de PageLayout puede referenciar cualquier objeto que lo implemente, y llamar al método Paginated fetchPagedNavigation hará que el objeto actualmente registrado sea instanciado. Si antes no haz utilizado interfaces, esto puede parecerte un poco confuso, pero, el resultado final, es que el código correcto será llamado y los resultados se distribuirán correctamente a lo largo de las páginas.

Para implementar esta técnica, todo lo que necesitas es crear un layout Estrategia que implemente la interfaz de PageLayout . Y luego, proveer una aplicación para el método fetchPagedLinks.

Este método tiene dos parámetros:

  1. $parent, que es el objeto Paginated
  2. $queryVars, que es la lista de parámetros de consulta para anexar a los números de página (opcional).

Hay tres puntos importantes a notar aquí:

Usted nunca deberá hacer llamadas directas a fetchPagedLinks; todos los métodos de Paginated pueden ser accedidos a través del objeto padre.

Si deseas utilizar tu propio layout, debes cambiar el diseño del resultado paginado a través de las llamadas a setLayout.

Con estos puntos en mente, vamos a crear nuestro propio diseño de páginas.

 Vamos a llamarlo TrailingLayout. Aquí está el código:

<?php
class TrailingLayout implements PageLayout {
public function fetchPagedLinks($parent, $queryVars) {
$currentPage = $parent->getPageNumber();
$totalPages = $parent->fetchNumberPages();
$str = "";
if($totalPages >= 1) {
for($i = 1; $i <= $totalPages; $i++) {
$str .= " <a href=\"?page={$i}$queryVars\">Page $i</a>";
$str .= $i != $totalPages ? " | " : "";
}
}
return $str;
}
}
?>

La clase anterior, TrailingLayout, implementa la interfaz PageLayout y provee la implementación para fetchPagedLinks. Recuerda que el parámetro $parent es una instancia del objeto Paginated, de modo que podemos determinar la página actual, y el número total de páginas, por la realización de llamadas a getPageNumber y fetchNumberPages, respectivamente.

En este simple diseño, una vez que hay más de una sola página, el script hará bucle a través del array de páginas, y creará un enlace y número de página para cada una. Las $queryVars también son escritas en el href como parte del bucle, el parámetro $queryVars resulta muy útil cuando se está paginando búsquedas. Los resultados de una búsqueda pueden haber incluido algunos pocos parámetros. Ten en cuenta que el string “Page” no es parte de las queryVars, pero está escrita por el bucle que añade los números de página del string.

Ahora vamos a probar la aplicación del nuevo diseño:

<?php
require_once "Paginated.php";
//include your customized layout
require_once "TrailingLayout.php";
//create an array of names in alphabetic order. A database call could have retrieved these items
$names = array("Andrew", "Bernard", "Castello", "Dennis", "Ernie", "Frank", "Greg", "Henry", "Isac", "Jax", "Kester", "Leonard", "Matthew", "Nigel", "Oscar");
$page = $_GET['page'];
$pagedResults = new Paginated($names, 10, $page);
echo "<ul>";
while($row = $pagedResults->fetchPagedRow()) {
echo "<li>{$row}</li>";
}
echo "</ul>";
//$pagedResults->setLayout(new TrailingLayout());
echo $pagedResults->fetchPagedNavigation("&firstLetter=l");
?>

Si ejecutáramos el script tal cual esta, nos daría el siguiente error.

"Fatal error: Call to a member function fetchPagedLinks() on a non-object".

Este error se produce porque aún no se ha registrado la estrategia que se desea utilizar antes de llamar a fetchPagedNavigation. Para cambiar el diseño de los enlaces de página, se pasa al método setLayout un parámetro, que puede ser cualquier objeto que implemente la interfaz de PageLayout. En el código de nuestro ejemplo anterior TrailingLayout, hay que descomentar la penúltima línea de código PHP, y actualizar la página para ver el resultado final, y que se muestra a continuación.

Paginación con PHP

La última línea de este código demuestra que el método fetchPagedNavigation puede tomar como parámetro opcional el string para definir el resto de la consulta (note la inclusión del ampersand antes de firstLetter del parámetro en la URL para distinguirlo del parámetro de la página.

En resumen

En este artículo hemos visto la Estrategia de Diseño de Patrones, que se puede utilizar cuando buscamos dar flexibilidad, especialmente cuando vayamos a editar los enlaces paginados.

Hemos visto este patrón en acción a través de la clase Paginated, que esperamos te resulte útil, cuando este mostrando sus datos en múltiples páginas. La clase se puede utilizar para mostrar arrays que contienen datos primitivos u objetos mucho más complejos.

Comentarios

Imagen de Carlos G
Carlos G

Hola, yo en lo personal uso un clase llamada kpaginate, es muy buena y fácil de usar, comparto con ustedes el link: paginar resultados con php.

Imagen de Juan Manuel De Castro

Páginas

Dejar comentario

Tutoriales

Cómo descargar videos de VK.com
En este artículo voy a explicar como descargar videos y películas...
Descargar Facebook Móvil Gratis
Por si aún no lo han hecho, es posible descargar Facebook Móvil...
Cómo generar tráfico web con las redes sociales - Paso a Paso
Muchas empresas están publicando contenidos como la forma de crear...

Artículo Recomendado

3 Tips cruciales para recuperar archivos eliminados
¿Te imaginas perder el trabajo de toda una semana en tan solo unos segundos? Todos hemos pasado por este problema. Quizás eliminamos por error un archivo importante o lo borramos sin pensar que era valioso para otro... más