Pagina navigatie in PHP en MySQL Pagina-navigatie met PHP en MySQL
Deze tutorial beschrijft het idee achter pagina-navigaties, en bespreekt stap voor stap
een uitwerking in PHP en MySQL.
1. Het idee
2. Benodigdheden
3. Waarden bepalen en berekeningen uitvoeren
3.1 Totaal aantal items
3.2 Maximaal aantal items per pagina
3.3 Het aantal pagina's
3.4 De huidige pagina onthouden
3.5 De juiste items opvragen
4. De navigatie
5. Navigatie netter / flexibeler maken
1. Het idee
Het principe van een pagina-navigatie is altijd hetzelfde: Je hebt een aantal items
(nieuws- of forumberichten, foto's, downloads, ...) die je over een aantal pagina's
wilt verdelen. Hierbij staan op elke pagina maximaal een van tevoren gedefineerd
aantal items. Om zo'n soort navigatiestructuur te creëren moet je een aantal zaken
bijhouden en berekenen. Deze gegevens zijn altijd ongeveer hetzelfde.
Het enige wat bij elke pagina-navigatie verschilt is de uitwerking, dus de naamgeving
van je navigatie-variabelen, het aantal links waarmee je naar de verschillende pagina's
kunt navigeren en het uiterlijk (gewone hyperlinks, selectbox, plaatjes, ...).
Omdat er duizend-en-één uitwerkingen mogelijk zijn, bespreken we hier alleen
het idee dat achter de navigatie-functionaliteit steekt. Met deze kennis zou je dan
in staat moeten zijn om je eigen variant te maken.
2. Benodigdheden
Allereerst maken we een inventarisatie van wat we allemaal nodig hebben. We maken
ons nog geen zorgen over hoe we al deze dingen moeten bepalen of berekenen - we stellen
eerst een lijst op van dingen die ons wel nuttig lijken om bij te houden:
- Het totaal aantal items dat we over een of meer pagina's willen verdelen
Dit gegeven is nuttig, omdat we zo op voorhand kunnen bepalen uit hoeveel pagina's
onze pagina-navigatie bestaat.
- Het maximaal aantal items per pagina
Dit gegeven is nodig om te bepalen over hoeveel pagina's je het totaal aantal items
verdeelt.
- Het aantal pagina's
Dit gegeven is af te leiden aan de hand van het totaal aantal items en het aantal items per pagina.
- De huidige pagina
Dit gegeven is nodig om te kunnen bepalen welke items getoond dienen te worden.
3. Waarden bepalen en berekeningen uitvoeren
Vervolgens gaan we eens rekenen aan deze gegevens. Hoe kunnen we de waarde van elk item
bepalen ? Bij het opslaan van waarden is het zeer handig als je variabelen omschrijvende
namen geeft.
3.1 Totaal aantal items
We gaan er van uit dat je items (gegevens) staan opgeslagen in een of andere database-tabel
genaamd "tabel". Deze items worden uniek geidentificeerd met een veld dat "id" heet.
We bepalen het totaal aantal items als volgt (hier zijn natuurlijk vele varianten op mogelijk,
het gaat om het principe):
<?php
$res1 = mysql_query("SELECT COUNT(id) FROM tabel") or die("res1:". mysql_error());
$items_totaal = mysql_result($res1, 0);
mysql_free_result($res1);
?>
Uitleg:
$res1 = mysql_query("SELECT COUNT(id) FROM tabel") or die("res1:". mysql_error());
Hiermee halen we het aantal id's (COUNT(id)) op uit tabel "tabel". In het die-gedeelte staat
het stukje string "res1:" om zo de query te kunnen identificeren wanneer er iets mis mocht
zijn met deze query. Het resultaat kennen we toe aan $res1.
We hoeven alleen maar het AANTAL items te weten, meer niet. Het resultaat is een resource
die precies één resultaatrij bevat, met één integer-waarde (namelijk het aantal items in tabel
"tabel").
Het is af te raden om op de volgende manier het aantal items te bepalen (SELECT *):
$res = mysql_query("SELECT * FROM tabel") or die(mysql_error());
$items_totaal = mysql_num_rows($res);
Met name wanneer tabel uit een heleboel items bestaat (wat je er waarschijnlijk in de eerste
plaats toe bewoog om deze items over meerdere pagina's te verdelen :)) haal je met deze
query ALLE gegevens op van de tabel, terwijl je alleen maar het AANTAL items in deze tabel
nodig hebt. $res bevat dus na afloop TIG records met ALLE gegevens van de tabel "tabel".
Vergelijk dit met het lezen van alle boeken in een bibliotheek, terwijl je alleen wilde weten
hoeveel boeken de bibliotheek bevatte... Een hoop verspilde moeite, tijd, CPU-cycles en geheugen
dus.
$items_totaal = mysql_result($res1, 0);
Lees het enige veld uit dat $res1 heeft (dit veld bevat het aantal items van tabel "tabel").
mysql_free_result($res1);
Geeft het resultaat van $res1 weer vrij - wel zo netjes. Je kan dit eventueel weglaten - alle
variabelen en resources worden bij beëindiging van het script toch vrijgegeven.
3.2 Maximaal aantal items per pagina
De waarde van deze variabele is puur gebaseerd op persoonlijke voorkeur. Je zou je navigatiescript
ook zo kunnen maken dat de gebruiker de keuze heeft uit een aantal waarden. We gaan er bij deze
tutorial van uit dat we 5 items per pagina willen afdrukken. De code wordt aldus:
<?php
$items_per_pagina = 5;
?>
Dit heeft hopelijk geen uitleg nodig.
3.3 Het aantal pagina's
Het aantal pagina's kun je, zoals al eerder gezegd, berekenen aan de hand van het totaal aantal
items en het aantal items dat je per pagina wilt afdrukken. Het wiskundig verband is als volgt:
<?php
$aantal_paginas = ceil($items_totaal / $items_per_pagina);
?>
ceil() rondt een getal naar boven af. Stel dat je 11 items hebt, en je wilt 5 items per pagina
afdrukken. Je hebt dan dus (11/5 = 2.2 = 3 naar boven afgerond) 3 pagina's nodig. Als je het
moeilijk vind om te een voorstelling van zaken te maken is het handig om een plaatje voor jezelf
te tekenen:
+--------------+--------------+--------------+
| pagina 1 | pagina 2 | pagina 3 |
+--------------+--------------+--------------+
items |01|02|03|04|05|06|07|08|09|10|11| | | | |
+--------------+--------------+--+-----------+
| totaal aantal items (11) |
+--------------+-----------------+
| items per |
| pagina (5) |
+--------------+
Hieruit kun je makkelijk afleiden dat je (minimaal) 3 pagina's nodig hebt.
Dit soort plaatjes zijn overigens altijd handig voor het begrip.
3.4 De huidige pagina onthouden
Er zijn meerdere manieren om te onthouden op welke "pagina" je je van een pagina met een
navigatiescript bevindt. We kiezen hier voor de variant waarbij de querystring van je URL
wordt gebruikt (oftewel het $_GET array, waarin alle variabelen en bijbehorende waarden
in paren zijn opgeslagen).
Wanneer we straks de hyperlinks gaan maken die naar de verschillende pagina's wijzen,
is het handig om naar een pagina te verwijzen via een vaste naam. We kiezen hiervoor de
querystring-variabele "p" - deze is dus te benaderen (als deze bestaat) onder de naam
$_GET['p'].
Om makkelijk van de waarde van deze variabele gebruik te maken, houden we deze ook in een
'gewone' variabele bij. We controleren altijd eerst of $_GET['p'] uberhaupt bestaat.
Als dit niet het geval is geven we onze 'gewone' variabele een default waarde. Dit kan op
de volgende wijze:
$huidige_pagina = 0;
if(isset($_GET['p']) && is_numeric($_GET['p']) && $_GET['p'] > 0 && $_GET['p'] < $aantal_paginas) {
$huidige_pagina = $_GET['p'];
}
Uitleg:
$huidige_pagina = 0;
We initialiseren $huidige_pagina op 0, voor het geval $_GET['p'] niet bestaat of geen zinnige
waarde bevat.
if(isset($_GET['p']) && is_numeric($_GET['p']) && $_GET['p'] > 0 && $_GET['p'] < $aantal_paginas) {
$huidige_pagina = $_GET['p'];
}
Wanneer $_GET['p'] bestaat en een numerieke waarde heeft, neemt de variabele $huidige_pagina
de waarde van $_GET['p'] over. We controleren hier ook meteen of $_GET['p'] binnen bestaande
pagina-nummers valt.
3.5 De juiste items opvragen
Nu we ongeveer alle gegevens hebben verzameld, word het tijd om de gegevens van de huidige
pagina op te vragen. Dit doen we weer met een mysql_query. We vragen nu alleen de gegevens
op die op deze pagina thuishoren.
Als we kijken naar het plaatje wat hierboven staat, dan willen we dus het volgende bereiken:
Wanneer we pagina 1 bekijken (of de pagina met pagina-navigatie voor het eerst opkomen) willen
we de items 1 t/m 5 weergeven.
Wanneer we pagina 2 bekijken, willen we de items 6 t/m 10 weergeven.
Wanneer we pagina 3 bekijken, krijgen we alleen item 11 te zien (er waren immers niet meer items).
We halen de voor die pagina relevante gegevens als volgt op (er van uitgaande dat we de NIEUWSTE
items als eerste willen afdrukken):
<?php
$offset = $huidige_pagina * $items_per_pagina;
$res2 = mysql_query("SELECT * FROM tabel ORDER BY id DESC LIMIT ".
$offset.",".$items_per_pagina)
or die("res2:". mysql_error());
?>
Uitleg:
$offset = $huidige_pagina * $items_per_pagina;
$offset is de startpositie in de database-tabel "tabel" waar begonnen moet worden met het ophalen van gegevens.
Stel dat we net op de pagina met pagina-navigatie binnenkomen. $_GET['p'] heeft dan nog geen waarde, dus
$huidige_pagina heeft dan de waarde 0. $items_per_pagina staat 'vast' op 5. $offset wordt aldus:
$offset = 0 * 5 = 0
We beginnen dus helemaal aan het begin van de database-tabel "tabel" te kijken naar items (de eerste
tabelrij heeft nummer 0).
Wanneer $huidige_pagina 1 is, wordt $offset 1 * 5 = 5 et cetera.
Let goed op het volgende:
In het script zelf worden de pagina's genummerd vanaf 0, omdat dit makkelijker is in verband met het
maken van queries, en het rekenen met pagina's. In de voor gebruikers zichtbare navigatie zullen de
pagina's genummerd zijn vanaf 1, omdat dit voor een gebruiker logischer oogt. We komen hier later nog
op terug.
$res2 = mysql_query("SELECT * FROM tabel ORDER BY id DESC LIMIT ".$offset.",".$items_per_pagina)
or die("res2:". mysql_error());
We zullen de query stap voor stap ontleden:
SELECT * FROM tabel
Selecteer 'alle' gegevens uit de database-tabel "tabel"
ORDER BY id DESC
Rangschik de resultaten in aflopende volgorde van "id" (nieuwste items staan vooraan).
Je zou ook een andere volgorde (bijvoorbeeld een alfabetische) kunnen gebruiken natuurlijk.
LIMIT ".$offset.",".$items_per_pagina
Het LIMIT-statement is in deze query van de vorm LIMIT a, b
waarbij a de startpositie is in de database (dit is $offset)
en b het aantal items is wat opgehaald dient te worden (dit is $items_per_pagina).
Op deze manier worden dus alleen de voor die pagina relevante items opgehaald en
is er dus geen sprake van verspilling van geheugenruimte.
Hierna kun je je items afdrukken met gebruikmaking van je favoriete mysql_fetch_...() methode.
4. De navigatie
Het laatste (en makkelijkste ?) onderdeel is het genereren van de navigatie-balk. Hier maak je
hyperlinks die naar de verschillende pagina's wijzen. In zijn eenvoudigste vorm ziet de navigatie-
balk er ongeveer als volgt uit:
<?php
for($i = 0; $i < $aantal_paginas; $i++) {
if($huidige_pagina == $i) {
// huidige pagina is niet klikbaar
echo "<b>".($i+1)."</b>";
} else {
// een andere pagina
echo "<a href="".$_SERVER['PHP_SELF']."?p=".$i."">".($i+1)."</a>";
}
// deel-streepje tussen alle items
if($i < $aantal_paginas - 1) {
echo " - ";
}
}
?>
Uitleg:
for($i = 0; $i < $aantal_paginas; $i++) {
We lopen alle pagina's een keer af
if($huidige_pagina == $i) {
// huidige pagina is niet klikbaar
echo "<b>".($i+1)."</b>";
Als dit if-statement 'waar' is, dan zijn we bij de pagina aangekomen waar we op dit moment op zitten.
Het is niet zinnig om hiervoor een klikbare link te maken, dus drukken we alleen het pagina-nummer af.
Let erop dat, zoals al eerder aangegeven, de "interne nummering" en de "externe nummering" 1 verschillen
(dit zie je ook terug in "$i+1").
} else {
// een andere pagina
echo "<a href="".$_SERVER['PHP_SELF']."?p=".$i."">".($i+1)."</a>";
}
We maken, wanneer het een link naar een andere dan de huidige pagina is, een hyperlink die bestaat uit
de naam van het script ($_SERVER['PHP_SELF']), gevolgd door "?p=" gevolgd door het (INTERNE!) pagina-
nummer ($i). De klikbare link heeft de tekst "$i+1" (het EXTERNE pagina-nummer).
// deelstreep tussen alle items
if($i < $aantal_paginas - 1) {
echo " - ";
}
Dit zorgt ervoor dat tussen alle pagina-nummers een deelstreep staat. Dit is niet verplicht, maar het
staat wat netter.
Compleet script
We gaan nu alles combineren tot één script. Dit script zal je nagenoeg altijd als uitgangspunt
voor je eigen navigatie-script kunnen gebruiken.
<?php
require("connect.php"); // maak verbinding met de database
$res1 = mysql_query("SELECT COUNT(id) FROM tabel") or die("res1: ".mysql_error()); // vraag het AANTAL items op
$items_totaal = mysql_result($res1, 0); // het totaal aantal items
mysql_free_result($res1); // geef het resultaat vrij
$items_per_pagina = 5; // vrij te kiezen
$aantal_paginas = ceil($items_totaal / $items_per_pagina); // het aantal items per pagina
// de huidige pagina opvragen
$huidige_pagina = 0; // default
if(isset($_GET['p']) && is_numeric($_GET['p']) && $_GET['p'] > 0 && $_GET['p'] < $aantal_paginas) {
$huidige_pagina = $_GET['p'];
}
// items van de huidige pagina ophalen
$offset = $huidige_pagina * $items_per_pagina;
$res2 = mysql_query("SELECT * FROM tabel ORDER BY id DESC LIMIT ".$offset.","
.$items_per_pagina) or die("res2:". mysql_error());
/*
doe hier iets met de gegevens in $res2
...
*/
// resultaten vrijgeven
mysql_free_result($res2);
// navigatie
for($i = 0; $i < $aantal_paginas; $i++) {
if($huidige_pagina == $i) {
// huidige pagina is niet klikbaar
echo "<b>".($i+1)."</b>";
} else {
// een andere pagina dan de huidige is wel klikbaar
echo "<a href="".$_SERVER['PHP_SELF']."?p=".$i."">".($i+1)."</a>";
}
// deel-streepje tussen alle items
if($i < $aantal_paginas - 1) {
echo " - ";
}
}
?>
5. Navigatie netter / flexibeler maken
Het bovenstaande voorbeeld is een simpel navigatiescript - het kan natuurlijk altijd
uitgebreider of netter. Wat je bijvoorbeeld zou kunnen doen, is alle bovenstaande
navigatie-code onderbrengen in een functie of een class, zodat je alleen maar een
functie- of methode-aanroep hoeft uit te voeren om een navigatie-menu te genereren.
Het principe blijft echter altijd hetzelfde.
We gaan hier nog kort in op een "moeilijkheid" die je hebt wanneer de pagina met de
navigatie-structuur zelf al een geinclude pagina is (bijvoorbeeld
?onderdeel=overzicht). Je hebt dan in het navigatie-menu-genereer gedeelte
niet genoeg aan enkel $_SERVER['PHP_SELF']."?p=".$i - je gooit dan namelijk de
$_GET-variabele "onderdeel" overboord, en dat is natuurlijk niet de bedoeling.
Dit probleem is eenvoudig op te lossen door gebruikmaking van een functie, die alle
$_GET-variabelen (BEHALVE $_GET['p'] !) achter elkaar zet in de vorm
?var1=waarde1&var2=waarde2 et cetera.
Hierachter plak je je pagina-navigatie-variabele met bijbehorende waarde.
We doen dit bijvoorbeeld als volgt:
<?php
function querystring($nav_var) {
$qstring = "?"; // de querystring
if(sizeof($_GET) > 1 || !in_array($nav_var, array_keys($_GET))) {
// meer queryvariabelen te onthouden dan enkel $_GET[$nav_var]
foreach($_GET as $k => $v) {
if($k != $nav_var) {
$qstring .= $k."=".$v."&";
}
}
}
return $qstring;
}
?>
Het deel om je navigatie te genereren wordt nu dus:
<?php
// navigatie
// scriptnaam + de querystring, met uitzondering van de navigatie-variabele
$q = $_SERVER['PHP_SELF'].querystring("p");
for($i = 0; $i < $aantal_paginas; $i++) {
if($huidige_pagina == $i) {
// huidige pagina is niet klikbaar
echo "<b>".($i+1)."</b>";
} else {
// een andere pagina - gebruik hier $q
echo "<a href="".$q."p=".$i."">".($i+1)."</a>";
}
// deel-streepje tussen alle items
if($i < $aantal_paginas - 1) {
echo " - ";
}
}
?>
|