Ir para o conteúdo

Sistema de paginação em PHP

Quando se tem um grande conjunto de resultados provenientes de uma consulta SQL, é útil apresentá-los de forma parcial, por questões de usabilidade ou até performance. Por exemplo, numa página de uma loja online, é comum que a apresentação de produtos após uma pesquisa esteja dividida em várias páginas. São inúmeras as vantagens de utilização de um sistema deste género, a título de exemplo:

  • O utilizador não tem de esperar tanto tempo para carregar a página.
  • Existe um menor volume de dados para processar por parte do servidor.
  • O utilizador não fica perdido numa imensidão de resultados.

Vamos então ver como fazer um sistema destes em PHP.

Criação de uma tabela simples

Para exemplificar o tutorial, vamos construir uma pequena tabela de pessoas. Na verdade, é possível usar qualquer tabela ou conjunto de tabelas, porque o que realmente importa é conseguir contar quantos resultados obtivemos.

CREATE TABLE pessoa (
  nome VARCHAR(50) NOT NULL
);

Introduzam alguns dados na vossa tabela, aqui fica um exemplo gerado com o GenerateData.com:

INSERT INTO pessoa (nome) VALUES ('Brody');
INSERT INTO pessoa (nome) VALUES ('Azalia');
INSERT INTO pessoa (nome) VALUES ('Fiona');
INSERT INTO pessoa (nome) VALUES ('Chantale');
INSERT INTO pessoa (nome) VALUES ('Sylvia');
INSERT INTO pessoa (nome) VALUES ('Hamilton');
INSERT INTO pessoa (nome) VALUES ('Colorado');
INSERT INTO pessoa (nome) VALUES ('Shelby');
INSERT INTO pessoa (nome) VALUES ('Julian');
INSERT INTO pessoa (nome) VALUES ('Hayfa');
INSERT INTO pessoa (nome) VALUES ('Nelle');
INSERT INTO pessoa (nome) VALUES ('Graiden');
INSERT INTO pessoa (nome) VALUES ('Jorden');
INSERT INTO pessoa (nome) VALUES ('Madeline');
INSERT INTO pessoa (nome) VALUES ('Hoyt');
INSERT INTO pessoa (nome) VALUES ('Paki');
INSERT INTO pessoa (nome) VALUES ('Bree');
INSERT INTO pessoa (nome) VALUES ('Russell');
INSERT INTO pessoa (nome) VALUES ('Victoria');
INSERT INTO pessoa (nome) VALUES ('Kaye');

Estratégia para paginação

Já temos a nossa tabela com alguns dados. Neste momento, se fizermos uma consulta à tabela, obtemos os 20 registos e podemos imprimir um a um na nossa página. Mas vamos supor que apenas pretendemos 5 registos por cada página. Como limitar os registos? O MySQL e a gereralidade dos SGBD têm a instrução LIMIT, que como o nome sugere, permite limitar um determinado conjunto de resultados. Por exemplo, experimentem a seguinte consulta:

SELECT * FROM pessoa LIMIT 0,5

Como podem ver, a consulta anterior devolve os primeiros 5 resultados. Em português corrente, podemos traduzir a consulta para algo como: "Devolva-me todos os valores da tabela pessoa. Desses resultados, apenas quero 5 registos, a começar do primeiro (0)".

Vamos ver outro exemplo:

SELECT * FROM pessoa LIMIT 10,5

Esta consulta pode traduzir-se por: "Devolva-me todos os valores da tabela pessoa. Desses resultados, apenas quero 5 registos, a começar do décimo primeiro (ou alternativamente, a partir do décimo, exclusive)".

Então, se tivermos LIMIT X,Y, X é o primeiro registo que deve ser devolvido (que começa em 0) e Y é o total de resultados a que queremos limitar. Esta é a base para perceber a paginação.

Aplicar o LIMIT

Recorrendo ao LIMIT, é fácil aplicarmos a paginação. Como sabemos, um sistema de paginação tem um número finito de páginas, que começam em 1 e a vão até N. Assumindo que queremos 6 registos em cada página, temos em primeiro lugar de calcular o total de páginas que pretendemos: basta pegar no total de registos da tabela, correspondentes à consulta, e dividir pelo número de registos por página, ou seja:

20 registos / 6 por página = 3,3333...

Portanto, precisamos de 3 páginas. Mas como temos um número não inteiro de páginas, temos sempre de arredondar por excesso. Ou seja, para que não fique nenhum registo por visualizar, iremos precisar de 4 páginas, neste caso.

Já sabemos quantas páginas precisamos de mostrar, mas agora, estando numa certa página, como saber que conjunto de resultados apresentar? Vamos usar o que aprendemos sobre o LIMIT para limitar os resultados. Neste caso, a consulta SQL genérica é:

SELECT * FROM pessoa LIMIT ((P - 1) * M), M

Em que:

  • M é o total de registos que queremos obter para uma página (no exemplo, são 6).
  • P é o número da página actual, que varia no intervalo de 1 até 4 no exemplo

Código PHP

Se experimentarem o seguinte código, devem obter os nomes que inseriram na tabela divididos por diversas páginas (tudo no mesmo script). Reparem que usámos a consulta SQL do ponto anterior para obter os registos diferentes consoante o número de página que estamos a visualizar. Reparem ainda que é criada uma pequena lista no fim do documento que permite saltar entre as diversas páginas.

<?php
// ligação à base de dados (não esquecer de trocar pelos dados correctos)
mysql_connect('hostsql', 'username', 'password') or die(mysql_error());
mysql_select_db('basedados');

// definir a constante com quantos registos queremos por página
define('POR_PAGINA', 6);

// contar o total de registos da nossa tabela e total de páginas
$sqlTotalRegistos = mysql_query('SELECT COUNT(*) FROM pessoa') or die(mysql_query());
$totalRegistos = mysql_result($sqlTotalRegistos, 0, 0);

$totalPaginas = ceil($totalRegistos / POR_PAGINA);

// obter o número de página actual, é a primeira por omissão
$pagina = 1;
if( !empty($_GET['pagina']) ){
    $p = intval($_GET['pagina']);

    if( $p <= 0 || $p > $totalPaginas ){
        header('Location: ?pagina=1');
        exit();
    }

    $pagina = $p;
}

// obter os registos para esta página, usando a consulta SQL genérica
$sqlPagina = mysql_query('SELECT * FROM pessoa LIMIT '.(($pagina - 1) * POR_PAGINA).' , '.POR_PAGINA) or die(mysql_error());

echo '<ul>';
while( $linha = mysql_fetch_row($sqlPagina) )
    echo '<li>'.$linha[0].'</li>';

echo '</ul>';

// gerar ligações para saltar entre páginas
for( $i = 1 ; $i <= $totalPaginas ; $i++ ){

    // Não criar uma ligação para a própria página que estamos a visualizar
    if( $i == $pagina )
        echo $i.' ';
    else
        echo '<a href="?pagina='.$i.'">'.$i.'</a> ';
}   
?>