Trabalhando com paginação usando Zend_Paginator

A paginação de coleções é um elemento crítico para interface de qualquer aplicação que forneça pesquisa ou outras formas de listagens, ao utilizar paginação, quebramos uma coleção em pedaços, onde cada pedaço é apresentado em uma página, impedindo que o usuário visualize uma quantidade exorbitante de itens, deixando a aplicação mais profissional.

O Zend Framework oferece um componente para realizar paginações, Zend_Paginator, que tem como princípio a flexibilidade, permitindo criar paginações a partir de vários tipos de coleções. Se você não trabalha utilizando frameworks, pode optar por desenvolver uma solução própria ou pesquisar uma solução na internet.

Fundamentos do Zend_Paginator

Este componente como foi dito é flexível e aceita diversos tipos de coleções, mas não se limita a sua flexibilidade, confira os objetivos do Zend_Paginator:

  • Paginar diversos tipos de dados, não apenas bancos de dados relacionais
  • Buscar apenas os resultados que precisam ser exibidos
  • Não forçar os usuários a utilizar apenas uma maneira de exibir os dados ou pagina-los
  • Ser fracamente acoplado aos outros componentes do Zend Framework

Para cada tipo de coleção de dados, existe um adaptador, ou seja, uma classe que fica responsável por fornecer acesso aos dados e controles de paginação, confira os adaptadores disponíveis:

Tipo Descrição
Array Utiliza um array como coleção de dados
DbSelect Utiliza uma instância DbSelect, retornando um array
DbTableSelect Utiliza uma instância DbTableSelect, retornando um rowset
Iterator Utiliza uma instância Iterator
Null Não manipula os dados, fornece apenas o controlador de páginas

Criando uma instância do Zend_Paginator

Para criar uma instância, primeiro devemos instanciar um tipo de adaptador, passando para o seu construtor nossa coleção, após instanciar nosso adaptador, basta instanciar o Zend_Paginator passando o adaptador como parâmetro no construtor da classe.

// Action scope...
$array = array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
$adapter = new Zend_Paginator_Adapter_Array($array);
$paginator = new Zend_Paginator($adapter);

Visando facilitar a vida do programador e padronizar a maneira que instânciamos este componente, podemos utilizar o método estático factory($data), que fica responsável por verificar o tipo de coleção que está sendo passado como parâmetro e utilizar o adaptador correto.

// Action scope...
$array = array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
$paginator = Zend_Paginator::factory($array);

Caso você utilize o adaptador do tipo Null, será necessário informar o número total de resultados, independente da maneira que você instanciar, seja pelo adaptador ou pelo método factory($data).

Configurações básicas do Zend_Paginator

Para personalizar a maneira que nossos dados serão apresentados ao usuário, o Zend_Paginator fornece métodos para configuração da paginação, confira alguns métodos disponíveis:

Método Padrão Descrição
setCurrentPageNumber 1 Informa o número da página atual
setItemCountPerPage 10 Informa o número de itens exibidos por página
setPageRange 10 Informa a quantidade de páginas visíveis na paginação
// Action scope...
$paginator->setCurrentPageNumber($this->_getParam('page'))
          ->setItemCountPerPage(15)
          ->setPageRange(15);

A quantidade de páginas pode variar dependendo do estilo escolhido, para esses casos este valor só será utilizado para iniciar o controle da paginação.

Manipulando os dados através do Zend_Paginator

Para manipular os dados, precisamos primeiro enviar nossa instância do Zend_Paginator para view.

// Action scope...
$this->view->assign('paginator', $paginator);

Com essa instância disponível na view, basta iterar utilizando um foreach e imprimir seus resultados.

// View scope...
<?php foreach ($this->paginator as $item): ?>
    <p>
        <?php echo $item ?>
    </p>
<?php endforeach; ?>

Esse tipo de manipulação não irá funcionar caso você utilize um adaptador do tipo Null.

Templates e estilos de rolagem

Até agora vimos como instanciar, configurar e manipular os dados da paginação, agora iremos dar uma olhada nos templates e estilos de rolagem utilizados para gerar os controladores de paginação.

Templates de paginação

O template de paginação é responsável por definir a estrutura utilizada para exibir as páginas disponíveis em uma determinada paginação, na documentação oficial deste componente são apresentadas algumas alternativas, mas você também pode criar seu próprio template.

Para isso contamos com uma série de propriedades disponíveis, confira:

Propriedade Tipo Descrição
first integer Número da primeira página
firstItemNumber integer Número absoluto do primeiro item desta página
firstPageInRange integer Primeira página do intervalo retornado pelo estilo de rolagem
current integer Número da página atual
currentItemCount integer Número de itens nesta página
itemCountPerPage integer Número máximo de itens disponíveis para cada página
last integer Número da última página
lastItemNumber integer Número absoluto do último item desta página
lastPageInRange integer Última página do intervalo retornado pelo estilo de rolagem
next integer Número da página seguinte
pageCount integer Número de páginas
pagesInRange array Conjunto de páginas retornadas pelo estilo de rolagem
previous integer Número da página anterior
totalItemCount integer Número total de itens

Estilos de rolagem

Além de permitir a criação de templates, o Zend_Paginator permite escolher estilos de rolagem para exibição das páginas, o estilo especifica como deve ser o comportamento do controlador enquanto navegamos pelas páginas.

Confira a lista de estilos disponíveis:

Tipo Descrição
All Exibirá todas as opções disponíveis
Elastic Parecida com a paginação do google, variando de tamanho
Jumping Exibe novas opções ao atingir o final das opções apresentadas
Sliding Tenta manter a página atual no centro da paginação, mais utilizado

Imprimindo o controlador de paginação

Para imprimir o controlador de paginação temos duas alternativas.

1. Utilizando o view helper paginationControl

// View scope ...
<?php echo $this->paginationControl($this->paginator,
                                    'Sliding',
                                    'pagination.phtml'); ?>
// ...

2. Configurando estilo e script no bootstrap ou action

// Bootstrap or Action scope ...
Zend_Paginator::setDefaultScrollingStyle('Sliding');
Zend_View_Helper_PaginationControl::setDefaultViewPartial('pagination.phtml');
// ...

// View scope ...
<?php echo $this->paginator; ?>
// ...

Caso seja adicionado no Bootstrap todos os paginators da aplicação serão configurados para utilizar esses valores como padrão, mas nada impede alterar casos específicos se necessário.

Trabalhando com paginação usando Zend_Paginator

Agora que já conhecemos um pouco sobre o Zend_Paginator, vamos criar uma paginação simples, para botar em pratica os conhecimentos deste artigo, que visa introduzir este tema. Baseado na estrutura apresentada no tópico Preparando o ambiente para desenvolvimento com Zend Framework, crie um projeto com nome de example-zend-paginator.

Estrutura inicial
Estrutura inicial

No exemplo que será visto agora, iremos utilizar uma tabela no banco de dados, será a tabela “user”, com dois campos user_id e name, além disso vamos precisar de alguns registros cadastrados para listar os resultados, para facilitar essa tarefa, acesse o script de criação e inserção de dados, copie este conteúdo, acesse o phpmyadmin, http://localhost/phpmyadmin, crie uma base de dados com o nome de “zf-paginator” e adicione o conteúdo do script para criar a tabela e seus registros.

Configure a aplicação para acessar o banco de dados “zf-paginator”, caso tenha dúvida, visualize o artigo Entendendo modelos no zend framework.

Criando nosso modelo

Crie o arquivo User.php na pasta “application/models” e adicione o seguinte conteúdo:

User.php

<?php
class User extends Zend_Db_Table_Abstract
{
    /**
    * The default table name
    */
    protected $_name = 'user';
}

Criando nosso controller e action

Neste exemplo iremos criar o controlador UserController.php contendo uma action, nomeada de list( listAction ), ou seja, utilizaremos a view list.phtml, o template que vamos utilizar será do tipo search, disponível na documentação do componente.

Adicione o arquivo “UserController.php” na pasta “application/controllers” com o seguinte conteúdo:

UserController.php

<?php
class UserController extends Zend_Controller_Action
{
    public function listAction()
    {
        $page = $this->_getParam('page', 1);

        $userModel = new User();
        $users = $userModel->fetchAll();

        $paginator = Zend_Paginator::factory($users);
        $paginator->setCurrentPageNumber($page)
                  ->setItemCountPerPage(10);

        Zend_Paginator::setDefaultScrollingStyle('Sliding');
        Zend_View_Helper_PaginationControl::setDefaultViewPartial('pagination.phtml');

        $this->view->assign('paginator', $paginator);
    }

}

Agora vamos preparar nossa view, crie uma pasta nomeada de “user” em “application/views/scripts” e depois adicione o arquivo list.phtml com o seguinte conteúdo:

list.phtml

<ul>
<?php if(sizeof($this->paginator)): ?>
    <?php foreach($this->paginator as $user): ?>
        <li><?php echo $this->escape($user->name); ?></li>
    <?php endforeach; ?>
<?php else: ?>
    <li>Nenhum usuário encontrado.</li>
<?php endif; ?>
</ul>
<?php echo $this->paginator; ?>

Nesse momento só falta nosso template de paginação, adicione o arquivo pagination.phtml em “application/views/scripts” com o seguinte conteúdo:

pagination.phtml

<?php if ($this->pageCount): ?>
<div>
<!-- Previous page link -->
<?php if (isset($this->previous)): ?>
    <a href="<?php echo $this->url(array('page' => $this->previous)); ?>">
        &lt; Previous
    </a> |
<?php else: ?>
    <span>&lt; Previous</span> |
<?php endif; ?>

<!-- Numbered page links -->
<?php foreach ($this->pagesInRange as $page): ?>
    <?php if ($page != $this->current): ?>
        <a href="<?php echo $this->url(array('page' => $page)); ?>">
            <?php echo $page; ?>
        </a> |
    <?php else: ?>
        <?php echo $page; ?> |
    <?php endif; ?>
<?php endforeach; ?>

<!-- Next page link -->
<?php if (isset($this->next)): ?>
    <a href="<?php echo $this->url(array('page' => $this->next)); ?>">
        Next &gt;
    </a>
<?php else: ?>
    <span>Next &gt;</span>
<?php endif; ?>
</div>
<?php endif; ?>

Estrutura final do nosso projeto:

Estrutura final
Estrutura final

Resultado

Ao acessar nossa action list do controller user, teremos o seguinte resultado:

Resultado Paginator, página 1
Resultado Paginator, página 1

Caso seja clicado no link para página 2 ou em next, iremos obter outra lista de resultado, confira:

Resultado Paginator, página 2
Resultado Paginator, página 2

Visualizar ou efetuar download do exemplo, lembrando que no repositório desse projeto no github não consta os arquivos do framework.

Este exemplo não está otimizado para casos de grande quantidade de registros, para saber como otimizar sua paginação, não deixe de visitar o próximo artigo: Otimizando paginações com Zend_Paginator.
  • Felipe Gama

    31/10/2011 às 12:01

    Parabéns Diogo, excelente!

    Ajudou muito cara, facilitou o entendimento de uma forma enorme…

    Obrigado.

    DEUS O ABENÇOE

  • Diogo Matheus

    01/11/2011 às 21:52

    Muito obrigado Felipe.

  • Wanderson

    06/12/2011 às 12:09

    Ótimo tópico Diogo,

    Mas dessa maneira se consome muito recurso, pois todos os dados da tabela são carregados.
    Tem como fazer a paginação como o Zend_Paginator de maneira a trazer somente os dados que realmente serão exibidos?

  • Diogo Matheus

    06/12/2011 às 12:32

    Olá Wanderson,

    Exatamente, está não é a melhor pratica para utilizar paginação com o Zend_Paginator.

    No final do artigo eu comento sobre isso, o objetivo desse artigo foi introduzir como realmente funciona o paginador, adaptadores e etc. O exemplo que botei é como costuma ser visto pela internet, no próximo artigo irei mostrar como criar paginações otimizadas.

    Mas adiantando, uma boa pratica seria passar um objeto da classe Zend_Db_Select, onde o adapter iria manipular o objeto adicionando limites baseados na página que deve ser exibida.

    Abraço e boa sorte.

  • Wanderson

    07/12/2011 às 14:01

    Beleza Diogo,

    Estarei sempre visitando seu blog para ver as novidades.

    Um grande abraço!

  • Diogo Matheus

    07/12/2011 às 14:03

    Valeu Wanderson, abraço.

  • Otimizando paginações com Zend_Paginator | Diogo Matheus Blog

    19/12/2011 às 08:57

    […] (typeof(addthis_share) == "undefined"){ addthis_share = [];} Continuando o artigo Trabalhando com paginação usando Zend_Paginator, no qual abordamos o uso básico de paginações com Zend_Paginator, neste artigo iremos falar […]

  • Thiago

    01/01/2012 às 02:49

    Diogo, otimas dicas!! Contudo, estou fazendo um site com uma relação de produtos com fotos, mas as fotos não carregam alem da primeira pagina! Sabe oque pode ser?
    ex:
    <img src="” />

    Abracos!!!

  • Flávio

    04/01/2012 às 11:47

    Parabéns pelo artigo. Me ajudou bastante

  • Diogo Matheus

    04/01/2012 às 11:55

    Thiago,
    Você está utilizando url absoluta? tente utilizar o helper baseUrl(); no endereço da imagem (src).

    Abraço e boa sorte.

  • Diogo Matheus

    04/01/2012 às 11:56

    Obrigado Flávio, que bom que te ajudou.

  • yure

    23/10/2013 às 16:02

    Tem como paginar com Scroll, deixando-o no estilo do facebook ? O zend oferece algum suporte quanto a isso ?

  • Joe

    02/07/2014 às 19:31

    Artigo TOP!!! Obrigado! Deus lhe abençoe!

  • Diogo Matheus

    26/02/2015 às 15:18

    Joe,
    Obrigado pelo comentáro.

  • Luiz Henrique Dapieve

    20/01/2017 às 13:23

    Olá Diogo, primeiramente quero agradecer pelo ótimo artigo.

    Enfrentei alguns problemas para encontrar uma solução com Paginator, e com esse artigo pensei em outra maneira de usar e estou postando esse comentário também para ajudar alguém.

    Estava construindo uma query bem complexa e com alguns innnerJoins e para facilitar eu precisava utilizar o “Doctrine::HYDRATE_SCALAR”, mas o paginator se perdia para fazer a contagem dos resultados então com seu artigo consegui resolver o problema da seguinte forma :

    $query = UserTable::getInstance()->list($options); (Retorna a Query)
    $query->setHydrationMode(Doctrine::HYDRATE_SCALAR);
    //Criação do Adapter para funcionar o Paginator com HYDRATE_SCALAR
    $adapter = new Zend_Paginator_Adapter_Array($query->execute());
    $paginator = new Application_Paginator_Paginator($adapter, $page);

    Espero que esse trecho também ajude quem precise.

    Obrigado. Abraço.

Deixe uma resposta

O seu endereço de e-mail não será publicado.. Campos obrigatórios são marcados com *