Vamos a ver como paginar registros con ZF2 esta es una herramienta muy importante dentro de un Framework. Esta librería en ZF2 puede paginar cualquier array, y como casi todas las herramientas que nos da Zend tienen varias formas de ser utilizada, unas más y otras menos complejas.
Paginar array
El paginador utiliza el adaptador ArrayAdapter para ejecutar la lógica de paginación. Lo que hace es recibir un array y lo pagina. Esta forma de paginar no es la más recomendable, para paginar datos desde una base de datos porque tendríamos que pasarle un array con todo el resulset con lo cual perdería el sentido la paginación porque seria una solución poco optima. Si tenemos 100 usuarios atacando a la misma base de datos y consultan una tabla con 100.000 registros se sacarían todos los registros de la tabla y solo se mostraran unos pocos, con lo cual el rendimiento cae mucho.
El modelo
<?php namespace Modulo\Model\Entity; use Zend\Db\TableGateway\TableGateway; use Zend\Db\Adapter\Adapter; use Zend\Db\Sql\Sql; use Zend\Db\Sql\Select; use Zend\Db\ResultSet\ResultSet; class UsuariosModel extends TableGateway{ private $dbAdapter; public function __construct(Adapter $adapter = null, $databaseSchema = null, ResultSet $selectResultPrototype = null){ $this->dbAdapter=$adapter; return parent::__construct('usuarios', $this->dbAdapter, $databaseSchema,$selectResultPrototype); } public function listarTodos($paginated=false){ $consulta=$this->dbAdapter->query("SELECT * FROM usuarios",Adapter::QUERY_MODE_EXECUTE); $datos=$consulta->toArray(); return $datos; } }
El controlador
<?php namespace Modulo\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; use Zend\Db\Adapter\Adapter; //Incluir modelos use Modulo\Model\Entity\UsuariosModel; class CrudController extends AbstractActionController{ private $dbAdapter; public function indexAction(){ return new ViewModel(); } public function paginacionAction(){ //Conexion $this->dbAdapter=$this->getServiceLocator()->get('Zend\Db\Adapter'); //Cargar modelo $usuarios=new UsuariosModel($this->dbAdapter); //Conseguir el valor id de la url y por defecto ponerle 1 $page=$this->params()->fromRoute("id",1); //Resulset a paginar $todos=$usuarios->listarTodos(); /* Creamos el objeto Paginator y le pasamos como parametro la instancia de otro objeto que nos permite paginar arrays */ $paginator = new \Zend\Paginator\Paginator(new \Zend\Paginator\Adapter\ArrayAdapter($todos)); /* Le decimos que la pagina actual sea la que le llega por la url, que saque dos registros por pagina y el numero de numeros que se van a mostrar como maximo */ $paginator->setCurrentPageNumber($page) ->setItemCountPerPage(2) ->setPageRange(7); //Renderizamos la vista $vista = new ViewModel(array( 'paginator'=>$paginator )); return $vista; } }
Esto es importante, hay que tener una vista exclusiva para mostrar la navegación de la paginación y añadirla al config del modulo.
Añadimos la vista al module.config.php
Creamos la vista de la navegación de la paginación, en el directorio layout.
Numpaginacion.phtml
<div class="paginationControl"> <a href="<?php echo $this->url('crud', array('action'=>'paginacion','id' => $this->first)); ?>">Primera </a>| <!-- Link para ir a la pagina anterior --> <?php if (isset($this->previous)){ ?> <a href="<?php echo $this->url($this->route, array('action'=>$this->action,'id' => $this->previous)); ?>"> < Previa </a> | <?php }else{ ?> <span class="disabled">< Previa</span> | <?php } ?> <!-- Links numerados --> <?php foreach ($this->pagesInRange as $id){ ?> <?php if ($id != $this->current){ ?> <a href="<?php echo $this->url($this->route, array('action'=>$this->action,'id' => $id)); ?>"> <?php echo $id; ?> </a> | <?php }else{ echo $id." | "; } } ?> <!-- Link para ir a la pagina siguiente --> <?php if (isset($this->next)){ ?> <a href="<?php echo $this->url($this->route, array('action'=>$this->action,'id' => $this->next)); ?>"> Siguiente > </a> <?php }else{ ?> <span class="disabled">Siguiente ></span> <?php } ?> |<a href="<?php echo $this->url('crud', array('action'=>'paginacion','id' => $this->last)); ?>"> Ultima</a> </div>
La vista:
Paginación.phtml
<h1>Paginación en Zend Framework 2</h1> <table class="table"> <tr> <th>ID</th> <th>Correo</th> <th>Contraseña</th> <th>Nombre y apellidos</th> </tr> <?php foreach($this->paginator as $usuario){ ?> <tr style="background-color:<?php echo $this->cycle(array("#F0F0F0","#FFFFFF"))->next()?>"> <td><?=$usuario["id"]?></td> <td><?=$usuario["email"]?></td> <td><?=$usuario["password"]?></td> <td><?=$usuario["nombre"]." ".$usuario["apellido"]?></td> </tr> <?php } ?> </table> <?php /* Imprimimos la navegación, le pasamos: 1. El paginador, el tipo de navegacion. 2. Le pasamos el tipo de navegacion (All, Elastic, Jumping, Sliding) 3. La ruta y la accion a la que pertenece la paginacion, esto hará que se construyan bien los enlaces */ echo $this->paginationControl($this->paginator,'Sliding','numpaginacion', array('route' => 'crud','action'=>'paginacion')); ?>
Iterator Adapter
De la misma forma que hemos visto en este ejemplo podemos utlizar el adaptador Iterator el cual hace prácticamente lo mismo.
Podriamos tener un metodo que nos haga la siguiente consulta.
public function listarTodos($paginated=false){ $resultSet = $this->select(function (Select $select){ $select->columns(array('id', 'name')); $select->order(array('id asc')); }); $resultSet->buffer(); $resultSet->next(); return $resultSet; }
Y en el controlador
$todos=$usuarios->listarTodos(); $paginator = new \Zend\Paginator\Paginator(new \Zend\Paginator\Adapter\Iterator ($todos));
Esta forma de paginar es algo mejor que la anterior, no es necesario pasarle un array con el resultSet sino que el lo saca. Si utilizamos el “ORM” que nos dá zf2 puede ser que sea mas optima que la anterior, aunque no estoy 100% seguro.
Paginación optima con DBSelect Adapter
Esta forma de paginar es la más correcta y eficiente cuando vamos a sacar datos de una BD, ya que el paginador se encarga de hacer los límites en la consulta para sacar solamente las filas necesarias, de forma que el rendimiento aumenta. Eso sí nos obliga a utilizar las capas de abstracción para las consultas, no podemos hacer una consulta en lenguaje SQL, lo cual nos garantiza por otro lado que la consulta estará lo mas optimizada posible.
“Este enfoque probablemente no le dará una enorme ganancia de rendimiento en pequeñas colecciones y / o consultas de selección sencillas. Sin embargo, con consultas complejas y de grandes colecciones, un enfoque similar podría darle un significativo aumento del rendimiento.” By Documentación oficial de Zend Framework 2
En un método de un modelo.
public function fetchAll2($currentPage = 1, $countPerPage = 2) { /*Objeto select para hacer consultas complejas utilizando métodos también nos serviría el objeto Sql o utilizar el método $sql = $this->getSql()->select(); para consultas simples, que solo nos permite utilizar la tabla que tengamos asignada la la clase actual (TableGateway) lo cual nos limita */ $select = new Select(); // Montamos una consulta compleja //Preparamos la subconsulta $subquery = new Select(); $subquery->from("siguiendo") ->columns(array("seguido")) ->where->equalTo("seguido",15)->or->equalTo("usuario", 15); //Consulta multitabla $select = new Select(); $select->from(array("p"=>"publicaciones")) ->columns(array('*')) ->join(array("u" =>'usuarios'), 'p.usuario=u.id_usuario', array("nick","nombre","apellidos","img_usu"=>"imagen")) ->where->in('p.usuario', $subquery); $select->order("p.id_publicacion DESC"); /* Instanciamos en el método del modelo el paginador con el adaptador DbSelect al que le pasamos el objeto select con la consulta y el adaptador de la base de datos */ $adapter = new \Zend\Paginator\Adapter\DbSelect($select, $this->dbAdapter); $paginator = new \Zend\Paginator\Paginator($adapter); //Le decimos la cantidad de elementos por pagina y la pagina actual $paginator->setItemCountPerPage($countPerPage); $paginator->setCurrentPageNumber($currentPage); //Devolvemos el paginador return $paginator; }
En un metodo action de un controlador.
public function pruebasAction(){ //Conseguimos el adaptador de la base de datos $this->dbAdapter=$this->getServiceLocator()->get('Zend\Db\Adapter'); //Le pasamos al modelo el adaptador $publicaciones=new PublicacionesModel($this->dbAdapter); //Conseguimos el parametro id de la url $page=$page=$this->params()->fromRoute("id",1);; /* El paginador nos llega desde este metodo del modelo, al que le pasamos la pagina actual y el numero de registros por pagina */ $paginator=$publicaciones->fetchAll2($page, 15); $paginator->setPageRange(7); //Se van a mostrar 7 numeros en la paginación // Le pasamos a la vista el paginador con los registros // la ruta y el metodo action en el que va a funcionar la paginacion $view=new ViewModel(array( "paginator"=>$paginator, "route" =>"actualizar", "action" =>"pruebas" )); return $view; }
Creamos la vista de la navegación de la paginación, en el directorio layout.
Numpaginacion.phtml
<div class="paginationControl"> <a href="<?php echo $this->url($this->route, array('action'=>$this->action,'id' => $this->first)); ?>">Primera </a>| <!-- Previous id link --> <?php if (isset($this->previous)){ ?> <a href="<?php echo $this->url($this->route, array('action'=>$this->action,'id' => $this->previous)); ?>"> < Previa </a> | <?php }else{ ?> <span class="disabled">< Previa</span> | <?php } ?> <!-- Numbered id links --> <?php foreach ($this->pagesInRange as $id){ ?> <?php if ($id != $this->current){ ?> <a href="<?php echo $this->url($this->route, array('action'=>$this->action,'id' => $id)); ?>"> <?php echo $id; ?> </a> | <?php }else{ echo $id." | "; } } ?> <!-- Next id link --> <?php if (isset($this->next)){ ?> <a class="siguiente" href="<?php echo $this->url($this->route, array('action'=>$this->action,'id' => $this->next)); ?>"> Siguiente > </a> <?php }else{ ?> <span class="disabled">Siguiente ></span> <?php } ?> |<a href="<?php echo $this->url($this->route, array('action'=>$this->action,'id' => $this->last)); ?>"> Ultima</a> </div>
En la vista
<?php foreach($this->paginator as $pub){ echo $pub["nick"]."<br/>"; echo $pub["texto"]."<br/>"; echo $pub["img"]."<br/>"; echo "<hr/>"; } //Imprimimos el paginador recibiendo los parametros pasados desde el controlador echo $this->paginationControl($this->paginator,'Sliding','numpaginacion', array('route' =>$this->route,'action'=>$this->action)); ?>
Más información:
Paginador en la documentación oficial de Zend Framework 2