Nesse artigo, irei falar sobre as melhores práticas para criar paginações usando Zend_Paginator, afinal não precisamos resgatar todos os registros de uma tabela para exibir N itens em cada página.
Confira os princípios de uma páginação otimizada:
- Buscar apenas os resultados que precisam ser exibidos
- Limitar registros retornados
- Ignorar registros já exibidos
- Obter total de registros afetados
O que tinha de errado na paginação do último artigo?
Basicamente no último artigo para criar a paginação foram resgatados todos os registros da tabela, através de um fetchAll(), dependendo do caso essa tabela poderia ter poucos registros ou milhares e o pior, pode ser que não esteja no mesmo servidor da aplicação, o que iria gerar mais lentidão para trazer todos esses registros.
Além disso, definimos o script e o tipo de paginação na action, o que fica um pouco deselegante, como diria Sandra Annenberg, no exemplo desse artigo iremos configurar no bootstrap da aplicação.
O fetchAll() não é recomendado para criar paginações, porque resgata todos os registros e consequentemente utiliza o adaptador Iterator para controlar os itens de cada página e de preferência defina script e tipo de paginação no bootstrap ou na view.
Solução: Adaptadores DbSelect e DbTableSelect
A solução básica para otimizar paginações de dados vindos do banco de dados com Zend_Paginator é utilizar os adaptadores DbSelect e DbTableSelect, a diferença entre eles é que o DbSelect retorna um array, já o DbTableSelect retorna um objeto rowset.
Como utilizar?
Para utilizar esses adaptadores, devemos passar um objeto da classe Zend_Db_Select ou Zend_Db_Table_Select como parâmetro, seja intanciando um dos adaptadores ou através do método estático factory() da classe Zend_Paginator.
Qual a vantagem desses adaptadores?
A grande vantagem é que o objeto passado como parâmetro é manipulado por esses adaptadores visando a página que será exibida, adicionando limite de registros baseado na configuração de itens por página e ignorando registros já exibidos caso seja necessário.
Mas ao fazer isso nosso resultado é limitado, se nossa paginação estiver configurada para 10 itens por página, será resgatado 10 itens apenas da tabela, logo, como iremos montar o controle de paginação? se só temos base em 10 registros. Agora entra a outra grande sacada, esses adaptadores criam uma query dinâmica apenas para calcular qual seria o total de itens afetados pela query original, ou seja, um count para calcular essa quantidade, com isso temos todos os ingredientes para uma paginação otimizada.
Confira os passos realizados pelos adaptadores:
(1) Primeiro precisamos de um objeto Zend_Db_Select ou Zend_Db_Table_Select.
(2) Quando o adaptador recebe esse objeto, ele verifica qual página será exibida, quantos itens por página deve buscar e manipula a query original para limitar a quantidade de registros que serão resgatados.
(3) Além disso o adaptador gera uma segunda query dinamicamente, baseada na original, para verificar quantos registros seriam afetados.
Podemos optar por uma contagem personalizada de registros afetados, para isso devemos utilizar o método setRowCount() do adaptador, onde podemos passar uma um objeto select ou um inteiro para forçar um determinado número como quantidade de registros afetados, nesse caso o adaptador não gera a query dinâmica.
Solução alternativa: Adaptador Null
Uma solução alternativa mas um pouco mais arriscada e complexa é utilizar o adaptador Null, que deixa quase tudo na mão do programador, resgatar os dados corretamente, enviar para view e iterar os dados. O adaptador fica responsável apenas por exibir o controle da paginação, para isso é necessário informar o total de registros afetados pela pesquisa na hora de instanciar o adaptador ou utilizar o método estático factory() da classe Zend_Paginator.
Dicas para melhorar a paginação
O que vimos até agora nada mais é do que o modo correto de criar uma paginação proveniente de banco de dados, que por mais simples que pareça é difícil encontrar exemplos na internet, mas não deixa de ser o uso otimizado de paginações, porque é mais do que pegar todos os dados e exibir determinados registros por páginas.
Para melhorar ainda mais nossas paginações podemos:
- Utilizar cache nas paginações
- Criar índices otimizados para as tabelas
Essas dicas ficarão para outro artigo, mas nada impede de você começar a pesquisar sobre o uso otimizado de índices ou cache.
Trabalhando com paginações otimizadas
Agora que já temos uma base de como otimizar paginações com Zend_Paginator, vamos criar uma paginação otimizada, para botar em pratica os conhecimentos deste artigo. Baseado na estrutura apresentada no tópico Preparando o ambiente para desenvolvimento com Zend Framework, crie um projeto com nome de example-advanced-paginator.
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, caso você tenha realizado o último exemplo pode pular essa parte.
Configure a aplicação para acessar o banco de dados “zf-paginator”.
Criando nosso modelo
No artigo anterior criamos um modelo básico, mas dessa vez como queremos enviar um objeto da classe Zend_Db_Table_Select para ser manipulado pelo paginator, iremos criar um método.
User.php
Definindo um padrão para as paginações
Nesse momento vamos definir um padrão no bootstrap da aplicação de como deverão ser as paginações, mas nada impede que isso seja alterado em casos específicos, de preferência utilizando o view helper paginationControl() quando necessário.
Bootstrap.php
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 de paginação 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
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
Só falta nosso template de paginação, adicione o arquivo pagination.phtml em “application/views/scripts” com o seguinte conteúdo:
pagination.phtml
Estrutura final do nosso exemplo:
Resultado
Ao acessar nossa action list do controller user, teremos o seguinte resultado:
Caso seja clicado no link para página 2 ou em next, iremos obter outra lista de resultado, confira:
Confira o código-fonte do exemplo no GitHub.
Aparentemente nada mudou, mas em questão de desempenho, nossa aplicação agradece, isso pode ser calculado verificando o tempo de execução dos scripts, não esqueça de adicionar bastantes registros antes de fazer os cálculos para ver a diferença.