login  Naam:   Wachtwoord: 
Registreer je!
 Tutorials

Tutorials > PHP


Gegevens:
Geschreven door:
Thomas
Moeilijkheidsgraad:
Normaal
Hits:
46122
Punten:
Aantal punten:
 (4.22)
Aantal stemmen:
36
Stem:
Niet ingelogd
Nota's:
 Lees de nota's (19)
 

Tutorial:

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."&amp;";
            }
        }
    }
    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 " - ";
    }
}
?>


« Vorige tutorial : OOP (Object Oriented Programming) Volgende tutorial : Javascript en PHP »

© 2002-2020 Sitemasters.be - Regels - Laadtijd: 0.037s