PHP en MySQL in de praktijk

De elementen van een taal of databasesysteem begrijpen is één, maar het wordt natuurlijk pas interessant als u er toepassingen mee kunt schrijven waar u ook echt iets aan heeft. Ik geef u een zetje in de rug door een volwaardige webtoepassing te behandelen. Als voorbeeld heb ik een gastenboek gekozen. Ook al zijn er genoeg gastenboeken die u gratis kunt gebruiken, om te leren programmeren is dit prima oefenmateriaal. We bouwen het gastenboek in de volgorde en op de manier die gebruikeljk is voor dit soort toepassingen. Zo krijgt u een beeld van hoe u te werk kunt gaan, mocht u na het lezen van deze pagina gelijk in het diepe willen springen.


Eerst denken, dan doen

De eerste programmeerles: weet wat u wilt. Zet eerst uw idee op papier en lees he nog eens door voordat u ook maar één regel code typt. Wat moet uw toepassing kunnen? Hoe moet die eruit zien? Wat mag er juist niet kunnen met uw toepassing? Hoe zou u als gebruiker de toepassing het liefst willen gebruiken?
Laten we eens kijken naar ons voorbeeld. De berichten in het gastenboek moeten natuurlijk zichtbaar zijn voor anderen. Bezoekers moeten nieuwe bijdragen kunnen plaatsen en ook die moeten worden opgeslagen. En het is wenselijk dat iedereen kan zien wie een bericht geplaatst heeft en wanneer dat was. Verder zou het leuk zijn als de auteur van een nieuwe bijdrage zijn/haar e-mailadres en eventueel ook een webadres kan achterlaten. E-mail en webadres moeten optioneel blijven, want niet iedereen wil dat soort informatie kwijt. De velden voor naam en bericht mogen gerust verplicht zijn - dat zijn wel de minste gegevens die u mag verwachten van een bezoeker.
We gaan twee scripts maken. Het eerste toont allle berichten in een HTML-pagina. Die pagina mag natuurlijk niet te lang worden, anders wordt de laadtijd wat lang en wordt de pagina onoverzichtelijk. Op de HTML-pagina komt ook een formulier om een nieuwe bijdrage te plaatsen. Het tweede script bewaart de ingevoerde gegevens in het gastenboek (lees: de database). Daarna stuurt dit script de auteur van een nieuwe bijdrage automatisch door naar een bijgewerkte HTML-pagina, waarin het nieuwe bericht bovenaan staat.


Plan van aanpak

Tijd om aan de slag te gaan. Allereerst moet u de berichten plaatsen in een database. Die zult u dus eerst moeten aanmaken. Het ontwerpen van die database is daarom de eerste stap in het ontwikkelproces. We zullen er wat records in plaatsen om te testen of de database goed genoeg is. Vervolgens gaan we het eerste script programmeren, dat de gegevens uit de database haalt en in een HTML-pagina toont. Als dat goed werkt, kunnen we een formulier inbouwen in die pagina en maken we een tweede script om de ingevoerde gegevens correct af te handelen.


De database ontwerpen

Start phpMyAdmin (of wat u ook gebruikt om uw MySQL-databases te beheren), want het is tijd om de database op te zetten. Een voor de hand liggende naam voor de database is simpelweg 'gastenboek'. In de database moet natuurlijk structuur komen in de vorm van tabellen. Deze toepassing is relatief simpel, dus kunnen we volstaan met slechts één tabel. In onderstaande listing ziet u de bijbehorende SQL-code.

CREATE TABLE berichten (
  bericht_id INT AUTO_INCREMENT PRIMARY KEY,
  afzender VARCHAR(50),
  email VARCHAR(100),
  url VARCHAR(100),
  bericht TEXT,
  berichtdatum DATETIME
);


De code 'CREATE TABLE berichten' maakt een nieuwe tabel met 'berichten' als naam. Tussen de ronde haakjes staan de velden die in de tabel moeten komen. Het eerste veld is 'bericht_id': een uniek, oplopend nummer waarmee elk bericht eenduidig te identificeren is. Hier is dit veld strikt genomen niet nodig, want voor onze toepassing hoeven we geen afzonderlijke records uit de database te halen. Maar met het oog op de toekomst kunnen we dit veld toch beter meenemen. Dan kunt u de toepassing later uitbreiden met bijvoorbeeld een extra mogelijkheid om als beheerder ongewenste berichten uit het gastenboek te verwijderen. In dat geval is unieke identificatie van een bericht wel noodzakelijk. En zo heeft u gelijk weer een idee om uw toepassing straks verder uit te bouwen. Verder ziet u de velden 'afzender', 'email', 'url', 'bericht' en 'berichtdatum', met hun bijbehorende veldtypes. Wat er in die velden komt, spreekt voor zich. Nu moeten we wat testgegevens in de tabel zetten, zodat u straks uw eerste script kunt testen. Hoe dat gaat ziet u in onderstaande listing.

INSERT INTO berichten
  (afzender, email, url, bericht, berichtdatum)
VALUES
  ('piet', 'piet@piet.nl', 'http://www.piet.nl', 'een test bericht', NOW());
INSERT INTO berichten
  (afzender, email, url, bericht, berichtdatum)
VALUES
  ('jan', 'jan@jan.nl', 'http://www.jan.nl', 'nog een test bericht', NOW());


Daar ziet u dat het veld 'bericht_id' in beide queries ontbreekt. De als AUTO_INCREMENT gedefinieerde velden krijgen namelijk automatisch een waarde, dus die hoeft u niet zelf te specificeren. Verder ziet u dat op de plaats van veld 'berichtdatum' alleen NOW() staat. Deze MySQL-functie zorgt ervoor dat de huidige datum en tijd in dat veld terechtkomen. Nu heeft u gegevens waarmee u kunt testen


Berichten tonen

Als u voor het eerst naar onderstaande listing kijkt, duizelt het misschien een beetje.

<html>
  <head>
    <title>Gastenboek<title>
  </head>
  <body>
  <form action="plaatsbericht.php" method="POST">
    Naam: <input type="text" name="afzender"><br>
    Email: <input type="text" name="email"><br>
    URL: <input type="text" name="url"><br>
    Bericht: <textarea name="bericht" cols="50" rows="4"></textarea><br>
    <input type="submit" value="plaats bericht">
  </form><br>

<?php
$db = mysql_connect("localhost", "gebruikersnaam", "wachtwoord");
mysql_select_db("gastenboek");

$berichten_query = mysql_query("
    SELECT afzender, email, url, bericht, berichtdatum FROM berichten
    ORDER by berichtdatum DESC LIMIT 30");

while($record = mysql_fetch_assoc($berichten_query))
{
    echo("afzender: {$record['afzender']}<br>");
    if(strlen($record['email']) > 0)
    {
        echo("Email: {$record['email']}<br>");
    }
    if(strlen($record['url']) > 0)
    {
        echo("URL: <a href='{$record['url']}'>{$record['url']}</a><br>");
    }
    echo("Bericht: {$record['bericht']}<br>");
    echo("Berichtdatum: {$record['berichtdatum']}<br>");
    echo("<br>");
}
?>
  </body>
</html>


Allereerst valt op dat tussen de HTML-codes zomaar een stuk PHP voorkomt. Kan dat zomaar? Jazeker, heel goed zelfs. Op het moment dat het script wordt aangeroepen, negeert PHP het HTML-gedeelte en voert alleen de PHP-code uit, waarna het resultaat van die code keurig tussen de overige HTML wordt ingevoegd. Verder ziet u in het HTML-gedeelte van bovenstaande listing een formulier, waarmee bezoekers nieuwe berichten in het gastenboek kunnen plaatsen. Hierover later meer.
Het PHP-gedeelte van bovenstaande listing maakt allereerst een verbinding met MySQL. De functie mysql_query() verzamelt alle records in de database en laat daarvan de gewenste velden zien. De records worden aflopend (DESC) gesorteerd op datum van plaatsing, zodat het nieuwste bericht bovenaan komt te staan. De SQL-code LIMIT 30 betekent dat er niet meer dan 30 records uit de database worden opgevraagd. Anders zou de lijst van berichten wel eens te lang kunnen worden, waardoor het laden van het gastenboek erg traag wordt.
Een zogeheten 'while-lus' verwerkt één voor één alle opgevraagde records. De voorwaarde die we hier aan while() meegeven, oogt misschien wat vreemd. U ziet dat het resultaat van de functie mysql_fetch_assoc() wordt toegekend aan de veriabele 'record'. Maar een toekenning aan een variabele is toch altijd waar? En dus gaat deze lus oneindig door? In dit geval niet. De functie mysql_fetch_assoc() haalt namelijk telkens een record uit de variabele 'berichten_query', net zolang tot er geen records meer over zijn. Zodra het einde is bereikt, zal mysql_fetch_assoc() de waarde false teruggeven en eindigt de lus.
In de lus geeft het script de inhoud van de record-array weer en plaatst er de HTML-tag <br> achter, zodat elk veld op de volgende regel komt. Wat extra HTML-codes zorgen ervoor dat een bezoeker van het gastenboek kan klikken op de url. Verder ziet u in de lus nog twee zogeheten 'if-statements' voor het tonen van de velden 'url' en 'email'. De functie strlen() kijkt of de lengte van de velden wel groter is dan 0. Als de lengte wel 0 zou zijn, had de auteur van het betreffende bericht blijkbaar geen url of e-mailadres ingevuld. In dat geval wordt het veld simpelweg niet getoond.


Nieuwe berichten plaatsen

Zoals u kunt zien was in het eerste script een formulier ingebouwd, die verwijst naar het script 'plaatsbericht.php'. Dit formulier gebruikt de zogeheten POST-methode. POST is simpel gezegd een manier om gegevens te versturen via het http-protocol, de basis van het World Wide Web. Dat is alles wat u daarover moet weten voor het bouwen van deze toepassing. In onderstaande listing ziet u de code om een bericht van het webformulier naar de database over te hevelen.

<?php
if(strlen($_POST['afzender']) < 1 || strlen($_POST['bericht']) < 1)
{
  echo("U heeft te weinig gegevens ingevuld. Ga terug.");
}
else
{
  $db = mysql_connect("localhost", "gebruikersnaam", "wachtwoord");
  mysql_select_db("gastenboek");

  $afzender = mysql_escape_string(strip_tags($_POST['afzender']));
  $email = mysql_escape_string(strip_tags($_POST['email']));
  $url = mysql_escape_string(strip_tags($_POST['url']));
  $bericht = mysql_escape_string(strip_tags($_POST['bericht']));

  mysql_query("
    INSERT INTO berichten
      (afzender, email, url, bericht, berichtdatum)
    VALUES
      ('{$afzender}', '{$email}', '{$url}', '{$bericht}', NOW())
  ");

  header("Location: http://website.nl/gastenboek.php");
}
?>


Het formulier wordt met de POST-methode verstuurd en dat heeft automatisch tot gevolg dat er in het script automatisch een associatieve array aanwezig is met de naam $_POST. In deze array zitten alle verstuurde gegevens, waarbij de veldnaam in het formulier telkens de sleutel is. $_POST['afzender'] bijvoorbeeld bevat de waarde die is ingevuld in het veld voor afzender.
Omdat we vooraf als eis hadden gesteld dat afzender en bericht verplichte velden zijn, moeten we eerst controleren of die velden wel een waarde bevatten. Dit gebeurt met de functie strlen(). Het if-statement zegt dus: "als de lengte van de waarde van het veld 'afzender' kleiner is dan 1". Wanneer dit zo is, zal het script een foutmelding laten zien. Is dit niet zo, dan bevatten de velden dus een geldige waarde en kunnen we verbinding maken met de database om ze te bewaren.
Dan gebeurt iets opmerkelijks. Twee functies verwerken alle binnengekomen waarden en stoppen het resultaat daarvan in nieuwe variabelen. Eén van de twee functies is mysql_escape_string(). Deze is belangrijk, omdat de binnengekomen waarde wel eens speciale tekens kan bevatten die een veiligheidsrisico vormen voor MySQL, bijvoorbeeld aanhalingstekens. Mysql_escape_string() codeert deze tekens en maakt de waarde 'veilig' om in de database te stoppen. De andere functie, strip_tags(), verwijdert alle PHP- en HTML-codes, zodat er geen ongewenste neveneffecten kunnen optreden als die gegevens later weer via een HTML-pagina op het scherm worden getoond. Pas als de gegevens veilig zijn, stopt een query, zoals u die in hoofdstuk 'de database ontwerpen' hebt gezien, ze in de database.
Vervolgens roept het script de functie header() aan. U hoeft voor het gastenboek niet het fijne van deze functie te weten. U kunt met behulp van header() een bezoeker doorsturen naar een bepaald webadres. In dit geval stuurt u de auteur van het nieuwe bericht door naar het script (met een fictieve domeinnaam) dat de berichten toont. Dit script haalt alle berichten op en toont de laatste bovenaan.


Websites

PHP/MySQL-referentie
MySQL-documentatie
PHP-documentatie


Met dank aan computertotaal!