Um dos principais obstáculos da criação de aplicativos Web flexíveis e fáceis de usar é o design de uma interface de usuário intuitiva. Muitos aplicativos tendem a crescer em tamanho e complexidade rapidamente, e designers e programadores acham que não conseguem lidar com a exibição de centenas ou milhares de registros. A refatoração leva tempo, e o desempenho e a satisfação do usuário podem sofrer.
A exibição de um número razoável de registros por página sempre foi uma parte crítica de todos os aplicativos e usada para causar muitas dores de cabeça aos desenvolvedores. O CakePHP facilita a carga para o desenvolvedor, fornecendo uma maneira rápida e fácil de paginar os dados.
A paginação no CakePHP é oferecida por um componente no controlador, para facilitar a
criação de consultas paginadas. A View Cake\View\Helper\PaginatorHelper
é usada para simplificar a geração de links e botões de paginação.
No controlador, começamos definindo as condições de consulta padrão que a paginação usará
na variável do controlador $paginate
. Essas condições servem como base para suas
consultas de paginação. Eles são aumentados pelos parâmetros sort
, direction
,
limit
e page
transmitidos a partir da URL. É importante notar que a chave order
deve ser definida em uma estrutura de matriz como abaixo:
class ArticlesController extends AppController
{
public $paginate = [
'limit' => 25,
'order' => [
'Articles.title' => 'asc'
]
];
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Paginator');
}
}
Você também pode incluir qualquer uma das opções suportadas
por find()
, como fields
:
class ArticlesController extends AppController
{
public $paginate = [
'fields' => ['Articles.id', 'Articles.created'],
'limit' => 25,
'order' => [
'Articles.title' => 'asc'
]
];
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Paginator');
}
}
Enquanto você pode passar a maioria das opções de consulta da propriedade
paginate, geralmente é mais fácil e simples agrupar suas opções de paginação
em Personalizando Metódos de Consulta. Você pode definir o uso da paginação do
localizador, definindo a opção finder
:
class ArticlesController extends AppController
{
public $paginate = [
'finder' => 'published',
];
}
Como os métodos localizadores personalizados também podem receber opções, é assim que você passa as opções para um método find personalizado dentro da propriedade paginate:
class ArticlesController extends AppController
{
// encontrar artigos por tag
public function tags()
{
$tags = $this->request->getParam('pass');
$customFinderOptions = [
'tags' => $tags
];
// o método find personalizado é chamado findTagged dentro de ArticlesTable.php,
// e deve ter se parecer com: public function findTagged(Query $query, array $options) {
// portanto, você usa tags como a chave
$this->paginate = [
'finder' => [
'tagged' => $customFinderOptions
]
];
$articles = $this->paginate($this->Articles);
$this->set(compact('articles', 'tags'));
}
}
Além de definir valores gerais de paginação, você pode definir mais de um conjunto de padrões de paginação no controlador, basta nomear as chaves da matriz após o modelo que deseja configurar:
class ArticlesController extends AppController
{
public $paginate = [
'Articles' => [],
'Authors' => [],
];
}
Os valores das chaves Articles
e Authors
podem conter todas as propriedades
que um modelo/chave menos a matriz $paginate
.
Depois de definida a propriedade $paginate
, podemos usar o método paginate()
para criar os dados de paginação e adicionar o PaginatorHelper `` se ainda não foi adicionado.
O método paginado do controlador retornará o conjunto de resultados da consulta paginada e
definirá os metadados de paginação para a solicitação. Você pode acessar os metadados da
paginação em ``$this->request->getParam('paging')
. Um exemplo mais completo do uso de
paginate()
seria:
class ArticlesController extends AppController
{
public function index()
{
$this->set('articles', $this->paginate());
}
}
Por padrão, o método paginate()
usará o modelo padrão para
um controlador. Você também pode passar a consulta resultante de um método find:
public function index()
{
$query = $this->Articles->find('popular')->where(['author_id' => 1]);
$this->set('articles', $this->paginate($query));
}
Se você quiser paginar um modelo diferente, poderá fornecer uma consulta para ele, sendo o próprio objeto de tabela ou seu nome:
// Usando a query.
$comments = $this->paginate($commentsTable->find());
// Usando o nome do modelo.
$comments = $this->paginate('Comments');
// Usando um objeto de tabela.
$comments = $this->paginate($commentTable);
Se você precisar paginar os dados de outro componente, poderá usar o PaginatorComponent diretamente. O PaginatorComponent possui uma API semelhante ao método do controlador:
$articles = $this->Paginator->paginate($articleTable->find(), $config);
// Ou
$articles = $this->Paginator->paginate($articleTable, $config);
O primeiro parâmetro deve ser o objeto de consulta de um objeto de localização na tabela
do qual você deseja paginar os resultados. Opcionalmente, você pode passar o objeto de
tabela e permitir que a consulta seja construída para você. O segundo parâmetro deve ser
a matriz de configurações a serem usadas para paginação. Essa matriz deve ter a mesma estrutura
que a propriedade $paginate
em um controlador. Ao paginar um objeto Query
, a opção
finder
será ignorada. Supõe-se que você esteja passando a consulta que deseja paginar.
Você pode paginar vários modelos em uma única ação do controlador, usando a opção
scope
na propriedade $paginate
do controlador e na chamada para o
método paginate()
:
// Propriedade Paginar
public $paginate = [
'Articles' => ['scope' => 'article'],
'Tags' => ['scope' => 'tag']
];
// Em um método do controlador
$articles = $this->paginate($this->Articles, ['scope' => 'article']);
$tags = $this->paginate($this->Tags, ['scope' => 'tag']);
$this->set(compact('articles', 'tags'));
A opção scope
resultará na PaginatorComponent
procurando nos parâmetros da
string de consulta com escopo definido. Por exemplo, o URL a seguir pode ser usado
para paginar tags e artigos ao mesmo tempo:
/dashboard?article[page]=1&tag[page]=3
Veja a seção paginator-helper-multiple para saber como gerar elementos HTML com escopo e URLs para paginação.
Para paginar o mesmo modelo várias vezes em uma única ação do controlador, é necessário definir um alias para o modelo. Consulte table-registry-usage para obter detalhes adicionais sobre como usar o registro da tabela:
// Em um método do controlador
$this->paginate = [
'ArticlesTable' => [
'scope' => 'published_articles',
'limit' => 10,
'order' => [
'id' => 'desc',
],
],
'UnpublishedArticlesTable' => [
'scope' => 'unpublished_articles',
'limit' => 10,
'order' => [
'id' => 'desc',
],
],
];
// Registre um objeto de tabela adicional para permitir a diferenciação no componente de paginação
TableRegistry::getTableLocator()->setConfig('UnpublishedArticles', [
'className' => 'App\Model\Table\ArticlesTable',
'table' => 'articles',
'entityClass' => 'App\Model\Entity\Article',
]);
$publishedArticles = $this->paginate(
$this->Articles->find('all', [
'scope' => 'published_articles'
])->where(['published' => true])
);
$unpublishedArticles = $this->paginate(
TableRegistry::getTableLocator()->get('UnpublishedArticles')->find('all', [
'scope' => 'unpublished_articles'
])->where(['published' => false])
);
Por padrão, a classificação pode ser feita em qualquer coluna não virtual que uma
tabela tenha. Às vezes, isso é indesejável, pois permite que os usuários classifiquem
em colunas não indexadas que podem ser caras de solicitar. Você pode definir a lista de
permissões dos campos que podem ser classificados usando a opção sortableFields
. Essa
opção é necessária quando você deseja classificar os dados associados ou os campos computados
que podem fazer parte da sua consulta de paginação:
public $paginate = [
'sortableFields' => [
'id', 'title', 'Users.username', 'created'
]
];
Quaisquer solicitações que tentem classificar campos que não estão na lista de permissões serão ignoradas.
O número de resultados que são buscados por página é exposto ao usuário como o
parâmetro limit
. Geralmente, é indesejável permitir que os usuários busquem
todas as linhas em um conjunto paginado. A opção maxLimit
afirma que ninguém
pode definir esse limite muito alto do lado de fora. Por padrão, o CakePHP limita
o número máximo de linhas que podem ser buscadas para 100. Se esse padrão não for
apropriado para a sua aplicação, você poderá ajustá-lo como parte das opções de paginação,
por exemplo, reduzindo-o para 10
:
public $paginate = [
// Outras chaves aqui.
'maxLimit' => 10
];
Se o parâmetro de limite da solicitação for maior que esse valor,
ele será reduzido ao valor maxLimit
.
Associações adicionais podem ser carregadas na tabela paginada usando o
parâmetro contains
:
public function index()
{
$this->paginate = [
'contain' => ['Authors', 'Comments']
];
$this->set('articles', $this->paginate($this->Articles));
}
O PaginatorComponent lançará uma NotFoundException
ao tentar acessar uma página
inexistente, ou seja, o número da página solicitada é maior que a contagem total de páginas.
Portanto, você pode permitir que a página de erro normal seja renderizada ou usar um
bloco try catch e executar a ação apropriada quando um NotFoundException
for capturado:
use Cake\Http\Exception\NotFoundException;
public function index()
{
try {
$this->paginate();
} catch (NotFoundException $e) {
// Faça algo aqui como redirecionar para a primeira ou a última página.
// $this->request->getParam('paging') fornecerá as informações necessárias.
}
}
Verifique a documentação Cake\View\Helper\PaginatorHelper
para saber como criar links para navegação de paginação.