No último post publicado, foi ensinado a maneira para poder listar os alunos e suas respectivas informações. Nesse atual post, será mostrado como fazer uma paginação padrão, limitada pelo tamanho de itens dentro da lista, podendo clicar no final se a página não for a página fim, podendo clicar no início caso a página atual não seja a página início, com espaçamento, caso seja uma paginação de uma lista grande, e tudo funcionando sem precisar recarregar a página, ou seja, utilizando AJAX e o PHP, utilizado para montar a lógica do funcionamento da paginação.
Em primeiro lugar, precisamos entender como funciona uma paginação básica:
- LIMIT
O comando LIMIT no SQL limita a quantidade de dados você quer que apareça, logo, se colocar no final da query um LIMIT 5
$sqlAlunos = "SELECT
aluno.id, aluno.nome, aluno.idade, aluno.media, status.nome AS status_nome, aluno.foto
FROM aluno
JOIN status ON aluno.idStatus = status.id
LIMIT 5";
Nós iremos receber apenas as primeiras 5 linhas
Esse dado é fixo, porém, iremos setar uma variável para armazenar seu valor:
$pageSize = isset($_GET['pageSize']) ? (int) $_GET['pageSize'] : 5; // Se não estiver setado, define o valor para 5
Agora, ao invés de usar LIMIT 5
, trocaremos por :pageSize
e diremos para o $stmt
que o valor de pageSize é $pageSize
e é um valor do tipo INT
:
$stmt->bindValue(':pageSize', $pageSize, PDO::PARAM_INT);
- Offset
Já cortamos a tabela em partes, mas agora, como será feito para ver as outras partes? É aí que entra o offset, ele permite que nós possamos começar a partir de um intervalo de linha, então se eu quero começar na linha 5, com um limite de 5 linhas, eu poderia usar no final da minha query:
LIMIT :pageSize OFFSET 5
E isso nos traria o que seria a próxima página:
Logo, mais uma vez teremos que adicionar mais umas variáveis, nesse caso, mais duas: $page
para definir a página atual que estamos naquele momento e $offset
, em resumo, se quisermos ver a 2º parte dos dados e assim consecutivamente precisaremos fazer uma simples equação $offset = ($page - 1) * $pageSize
, lembrando que a primeira página é a página 1, e precisamos pegar desde a linha 0, por isso $page - 1
e a variável $page que será $page = isset($_GET['page']) ? (int) $_GET['page'] : 1;
Agora, poderemos adicionar ao final da query:
LIMIT :pageSize OFFSET :offset
Ou simplificando:
LIMIT :offset, :pageSize
E por último, entregar para o $stmt
o valor de :offset
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
- LÓGICA
Para começar, devemos contar qual é o total de registros que temos para descobrir a quantidade de páginas que o sistema possuirá, assim possibilitando para que nos retorne a primeira e a última página na paginação sempre.
$sqlCount = "SELECT COUNT(*) FROM aluno";
$stmtCount = $pdo->prepare($sqlCount);
$stmtCount->execute();
$totalRegistros = $stmtCount->fetchColumn();
$totalPaginas = ceil($totalRegistros / $pageSize) // a função ceil retorna o valor acima mais próximo, caso seja um número quebrado.
// vão ser o intervalo do meu for loop
$start = max(1, $page - 2);
$end = min($totalPaginas - 1, $page + 2);
O que é necessário saber sobre a lógica a seguir:
- Se a página atual for maior do que 3, então ele volta com o 1 no início, já que é a partir do 3 que o intervalo entre
$start
e$end
não pegam mais o número 1; - Entre
$start
e$end
, ele deve imprimir todos os números presentes dentro do intervalo. Se a variável do loop$i
for igual à página atual$page
, logo, o número fica em destaque dos outros. Se não, ele apenas printa o número na tela - Enquanto a página atual
$page
é menor que o total de páginas - 3, ele irá printar "..." em seguida, representando que tem continuidade. - Se o total de páginas
$totalPaginas
for maior do que 1 e a página atual for diferente da última página que também é representado pela variável$totalPaginas
, ele irá printar a página final, sem estar em destaque.
echo "<div class='paginacao'>";
if ($page > 3){
echo "<a href='#' data-page='1'> 1 </a>";
echo "<span> ... </span>";
}
for ($i = $start; $i <= $end; $i++){
if ($i === $page){
echo "<a href='#' data-page='$i'>
<strong> $i
</strong></a>";
} else {
echo "<a href='#' data-page='$i'> $i </a>";
}
}
if ($page < $totalPaginas - 3){
echo "<span> ... </span>";
}
if ($totalPaginas > 1 && $page != $totalPaginas){
echo "<a href='#' data-page='$totalPaginas'> $totalPaginas </a>";
}
echo "</div>";
Como resultado, teremos isso aqui:
Porém, ele ainda não está completo, ainda precisamos inserir a lógica com ajax para que ele funcione e não precise recarregar a página toda vez
- AJAX
AJAX é "JavaScript Assíncrono e XML", ele faz requisições em segundo plano para o servidor sem que seja necessário recarregar todo o html da página, permitindo que o usuário, no caso atual, acesse os outros dados de maneira assíncrona, recebendo apenas os dados que foram pedidos instantaneamente.
Para isso, crie dois arquivos. Um que será parte do front: index.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lista de Alunos</title>
</head>
<body>
<h2>Alunos</h2>
<div id="resultado">
<?php include 'artigos.php'; ?>
</div>
<script src="./app.js"></script>
</body>
</html>
E outro de nome app.js
:
document.addEventListener('DOMContentLoaded', function () {
const resultado = document.getElementById("resultado");
document.addEventListener('click', function (e) {
if (e.target.matches('[data-page]')) {
e.preventDefault();
const page = e.target.getAttribute('data-page');
fetch(`artigos.php?page=${page}&ajax=1`)
.then(res => res.text())
.then(html => {
resultado.innerHTML = html;
})
}
});
});
Ele funciona da seguinte forma:
document.addEventListener('DOMContentLoaded', function ()
: espera o carregamento completo da página para executar alguma função;const resultado = document.getElementById("resultado");
: seleciona onde serão feitas as requisições que irão envolver o ajax;document.addEventListener('click', function (e)
: adiciona um listener para todos os botões na página, isso se chama event delegation, ao invés de pegar botão por botão, ele pega todos os botões para depois tratar eles;if (e.target.matches('[data-page]'))
: se o elemento clicado tiver o atributodata-page
;e.preventDefault();
: evita comportamento padrão de recarregar a página;const page = e.target.getAttribute('data-page');
: pega o valor do atributodata-page
para adicioná-lo na requisição ajax;fetch('artigos.php?page=${page}&ajax=1\')
: faz a requisição a página de artigo correspondente, oajax
é para dizer que somente deve ser entregue o conteúdo solicitado;.then(res => res.text())
: como a resposta não é em JSON, usamos .text;.then(html => {resultado.innerHTML = html;})
: atualiza a div resultado.
Por fim, a paginação após isso deve estar funcionando de forma assíncrona, sem recarregar a página e entregando todos os conteúdos solicitados.
Na próxima parte, irei mostrar como fazer uma barra de pesquisa usando também ajax. Muito obrigado por lerem até aqui!
O repositório oficial do projeto se encontra aqui: https://github.com/Luis-60/lista-de-alunos.