Introduzione
La paginazione rappresenta, probabilmente, il primo scoglio "serio" che si trova ad affrontare chi comincia ad approcciarsi all'interazione tra un linguaggio di scripting server-side ed un Database (che nel nostro caso saranno, ovviamente, PHP E MySql). Ma cosa si intende per paginazione ?
Letteralmente questa parola indica la suddivisione in più pagine del risultato restituito da un'interrogazione ad un Database.
Pensiamo all'esempio classico di un motore di ricerca. Se digitassimo, ad esempio, la parola "web" verremmo sicuramente sommersi da decine di migliaia di risultati che, per ovvie ragioni, non potrebbero essere visualizzati tutti in un'unica pagina; da qui l'esigenza, appunto, di suddividere questi risultati, mostrandone quindi un tot per pagina così da potersi muovere più agevolmente fra gli stessi.
Prima di andare avanti, mi sento in dovere di chiarire che gli esempi ed il codice che mostrerò riguardano PHP 4 e, soprattutto, MySql 3.2.23.xx (l'ultima versione stabile rilasciata al momento in cui scrivo).
Esistono due approcci al problema della paginazione con PHP e MySql, il primo è quello che prevede l'uso della clausola LIMIT e il secondo è, ovviamente, quello che non la prevede. Entrambi hanno pro e contro ed io personalmente prediligo il primo perchè, a mio modesto avviso, consente di gestire molto più facilmente il tutto, anche se , per contro, richiede l'esecuzione di due distinte query: una che determini il numero totale di risultati (senza LIMIT) ed un'altra che estragga i risultati che ci interessano. Entrambe, ovviamente, conterranno le medesime condizioni all'interno della clausola WHERE (ammesso che questa sia presente).
La clausola Limit
La clausola LIMIT cui si è accennato, ha questa sintassi:
LIMIT valoreinizio, numerorecords
e costituisce una variazione di MySql alla sintassi SQL di SELECT definita dall'ANSI nello standard SQL99 che limita il numero di records restituiti dalla query, mediante il valore specificato da valoreinizio e la quantità espressa da numerorecords.
Sulla clausola LIMIT vale la pena di soffermarsi un attimo, perchè essa costituisce il fulcro essenziale della paginazione. Sarà sufficiente giocare correttamente con i suoi due parametri per potere agevolmente suddividere i risultati in più pagine.
Facciamo un esempio per poterla capire meglio, ipotizzando una semplicissima query:
SELECT * FROM tabella WHERE nome LIKE 'g%' LIMIT 0, 10
Senza la clausola LIMIT, l'esecuzione di questa query avrebbe restituito tutti i records che avessero soddisfatto la condizione espressa all'interno di WHERE (ovvero con g come prima lettera del contenuto del campo nome). Con la sintassi LIMIT 0, 10 invece, riusciremo ad ottenere i primi dieci risultati (importante a questo proposito notare che LIMIT parte da zero e non da uno).
Come fare per ottenere tutti gli altri risultati, o meglio i successivi dieci (ammesso che ve ne siano)? Molto semplice, è sufficiente modificare il primo parametro di LIMIT (nel presupposto che se ne vogliano visualizzare sempre 10 per pagina, altrimenti si può cambiare anche il secondo parametro). La query diventerebbe quindi questa:
SELECT * FROM tabella WHERE nome LIKE 'g%' LIMIT 10, 10
In questo modo otterremmo dall'undicesimo al ventesimo record prodotto dall'interrogazione e così via. Un problema però si pone a questo punto: fino a quando dovrà andare avanti l'incremento del primo parametro di LIMIT ?
Ecco perchè, come accennato all'inizio, abbiamo bisogno di una prima query (senza LIMIT) che ci consenta di conoscere il numero esatto di records prodotti dalla nostra interrogazione, per poter quindi sapere con esattezza su quante pagine verranno distribuiti i records.
Esempio concreto
Per poter paginare i risultati di un'interrogazione, abbiamo quindi bisogno di conoscere poche semplici cose:
- il numero totale di risultati restituiti dalla query (cosa che otteniamo con la prima query)
- il numero di risultati da visualizzare per pagina (il secondo parametro di LIMIT)
- il numero totale di pagine (da determinare in funzione dei due precedenti parametri, peraltro non indispensabile)
Sulla base di questa considerazione, possiamo cominciare a costruire uno schema base di paginazione, con l'avvertenza che, come vedremo meglio in seguito, l'essenziale è capirne il meccanismo perchè, una volta colto questo, l'unico limite è dettato dalla fantasia del programmatore.
Il codice della pagina che esegue l'interrogazione al database e mostra i risultati, ridotto all'essenziale, potrebbe essere questo:
<?php
// connessione al database
mysql_connect("host", "user", "password") or die("Connessione fallita !");
// selezione del DB
mysql_select_db("nome_db") or die("Selezione del DB fallita !");
// esecuzione prima query
$count = mysql_query("SELECT COUNT(id) FROM nome_tabella");
$res_count = mysql_fetch_row($count);
// numero totale di records
$tot_records = $res_count[0];
// risultati per pagina(secondo parametro di LIMIT)
$per_page = 10;
// numero totale di pagine
$tot_pages = ceil($tot_records / $per_page);
// pagina corrente
$current_page = (!$_GET['page']) ? 1 : (int)$_GET['page'];
// primo parametro di LIMIT
$primo = ($current_page - 1) * $per_page;
echo "<div align="center">n<table>n";
// esecuzione seconda query con LIMIT
$query_limit = mysql_query("SELECT id, nome FROM nome_tabella LIMIT $primo, $per_page");
while($results = mysql_fetch_array($query_limit)) {
echo " <tr>n <td>";
echo "<a href="page.php?id=" . $results['id'] . "">" . $results['nome'] . "</a>
";
echo "</td>n </tr>n";
}
// includiamo uno dei files contenenti la paginazione, commentate l'altro ovviamente
include("paginazione_1.php");
//include("paginazione_2.php");
// in questa cella inseriamo la paginazione
echo " <tr>n <td height="50" valign="bottom" align="center">$paginazione</td>n";
echo " </tr>n</table>n</div>";
mysql_close();
?>
Al di là della connessione, della selezione del DB, e delle query in sè (le quali, ripetiamo, devono contenere la medesima clausola WHERE, se questa è prevista dallo script), la cui spiegazione esula dagli scopi di questo articolo, voglio rimarcare poche semplici cose su cui la paginazione si basa.
Anzitutto, memorizziamo nella variabile $tot_pages il numero totale di risultati e nella variabile $per_page il numero di records che vogliamo mostrare per pagina (il secondo parametro di LIMIT). In base a queste due variabili (ed alla funzione ceil() che arrotonda la divisione all'intero superiore), determiniamo quante saranno le pagine totali in cui suddividere il risultato dell'interrogazione (variabile $tot_pages).
Settiamo quindi la variabile $current_page, ossia la pagina corrente che, all'inizio sarà uguale a 1 e, successivamente avrà il valore di $_GET['page'] (che passeremo via GET con il metodo di paginazione scelto), ed infine la variabile $primo (il primo parametro di MySql), la più importante a ben vedere, che sarà uguale a ($current_page - 1) * $per_page; nella prima pagina avrà valore zero, nella seconda dieci e così via (come spiegato nell'esempio iniziale).
Un'ultima cosa da rimarcare riguarda il fatto che il codice che materialmente esegue la paginazione, si trova in un file esterno, o meglio in due files (paginazione_1.php o paginazione_2.php) perchè è mia intenzione illustrarvi due dei possibili metodi per paginare i record, ribadendo ulteriormente che potete inventarvene di altri molto più complessi o aderenti a specifiche necessità, basta capire il metodo e lavorare un pò di fantasia.
Due metodi per paginare
In conclusione, ecco il contenuto dei files esterni contenenti il codice per la paginazione:
Primo metodo
<?php
if($current_page == 1) { // se siamo nella prima pagina
$precedente = "<< precedente";
} else { // altrimenti
$previous_page = ($current_page - 1);
$precedente = "<a href="?page=$previous_page" title="Vai alla pagina precedente"><< precedente</a>";
}
if($current_page == $tot_pages) { // se siamo nell'ultima pagina
$successiva = "successiva >>";
} else { // altrimenti
$next_page = ($current_page + 1);
$successiva = "<a href="?page=$next_page" title="Vai alla pagina successiva">successiva >></a>";
}
$paginazione = "$precedente $successiva";
?>
Secondo metodo
<?php
$paginazione = "Pagine totali: " . $tot_pages . "
[";
for($i = 1; $i <= $tot_pages; $i++) {
if($i == $current_page) {
$paginazione .= $i . " ";
} else {
$paginazione .= "<a href="?page=$i" title="Vai alla pagina $i">$i</a> ";
}
}
$paginazione .= "]";
?>
Il primo sistema si limita a mostrare due scritte, << precedente e successiva >>, le quali saranno cliccabili o meno a seconda, ovviamente, che ci si trovi o meno nella prima o nell'ultima pagina.
Il secondo metodo, invece, è probabilmente più elegante e mostra tutti i numeri delle singole pagine, anch'essi cliccabili o meno a seconda che il numero di pagina coincida con quella corrente.