Tutorials

PDO - Verbinden met verschillende databases

Gebruik de PDO extensie om verbindingen met verschillende database systemen te onderhouden.

Pagina 1

Inleiding

Tegenwoordig kun je er bij het ontwikkelen van een website bijna niet meer omheen: het gebruik van een database. Of je nu bezig bent met een persoonlijke website met gastenboek, een site voor je bedrijf met support forum of met een ingewikkelde webshop, de basis van elke goede website wordt gevormd door een database.

Er zijn vrij veel verschillende database systemen die je voor dit doeleinde kunt gebruiken en allemaal kennen ze hun eigen functies. Aangezien elke database anders is, vereist het werken ermee in PHP steeds een andere aanpak. Voor de meest gebruikte database systemen waaronder MySQL, PostgreSQL en SQLite, biedt PHP standaard ondersteuning. Echter zul je voor het benaderen van deze verschillende databases altijd nog verschillende functies gebruiken.

Met de komst van PHP Data Objects (PDO) in PHP 5.1 hebben we de beschikking over een uiterst handige extensie voor het benaderen van verschillende databases. Naast het gebruik van een object georiënteerde notatie biedt deze extensie ook nog andere voordelen. Het wordt namelijk een stuk eenvoudiger om resultaat sets te verwerken, prepared statements uit te voeren en transactions te gebruiken.

In deze tutorial zal ik uitgebreid ingaan op de werking van de PDO extensie. Ik zal ingaan aan de hand van vele voorbeelden ingaan op de meest belangrijke functies. Ik ga er in deze tutorial wel vanuit dat je al over de benodigde basiskennis beschikt.

Benodigde voorkennis
[li]Basis PHP[/li]
[li]Werken met databases in PHP[/li]
[li]Basis SQL[/li]
[li]Basiskennis van object georiênteerd programmeren[/li]
Pagina 2

Gebruik van de PDO extensie

Voor het gebruik van de PDO extensie moeten we natuurlijk wel eerst weten of de database die we willen gebruiken wel ondersteund wordt. Om PDO met een bepaalde database te gebruiken, moeten we de juiste PDO driver geînstalleerd hebben.

Voorbeeld 1: Controleren welke databasedrivers geïnstalleerd zijn
<?php
echo '<pre>';
print_r(PDO::getAvailableDrivers());
echo '</pre>';
?>
In mijn geval levert dat het volgende resultaat:

Array
(
    [0] => sqlite2
    [1] => pgsql
    [2] => mysql
)

Dit betekent dat ik op dit moment de juiste drivers geînstalleerd heb om te werken met de database systemen MySQL, PostgreSQL en SQLite.

Maar ook andere populaire databases worden door PDO ondersteund zoals in onderstaand lijstje te zien is:

[li]MySQL[/li]
[li]PostgreSQL[/li]
[li]SQLite[/li]
[li]Microsoft SQL Server[/li]
[li]ODBC[/li]
[li]Oracle[/li]

Verbinden met een database
Het verbinden met een database met behulp van PDO is kinderlijk eenvoudig. Het komt eigenlijk alleen neer op het aanmaken van een nieuwe instantie van de PDO klasse. Natuurlijk moet je bij het instantieren van de klasse wel de juiste paramters meegeven.

Onderstaande voorbeelden laten van enkele databases zien hoe er een verbinding mee gemaakt wordt.

Voorbeeld 2: Een MySQL verbinding opzetten
<?php
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
?>
Voorbeeld 3: Een pgSQL verbinding opzetten
<?php
$db = new PDO('pgsql:host=localhost;dbname=test', 'user', 'password');
?>
Voorbeeld 4: Een SQLite verbinding opzetten
<?php
$db = new PDO('sqlite:/path/to/database.sdb');
?>
Voorbeeld 5: Een Oracle verbinding opzetten
<?php
$db = new PDO('oci:', 'user', 'password');
?>
Dat je voor het opzetten van een verbinding met elke willekeurige database telkens dezelfde regel PHP kunt gebruiken, heeft te maken met het feit dat de eigenlijke verbinden met de database achter de schermen afgehandeld wordt. De meegegeven parameters worden door de extensie geinterpreteerd en een verbinding met de juiste database wordt gemaakt.

Sluiten van een verbinding
Ook het sluiten van een verbinding is kinderlijk eenvoudig en voor elke database hetzelfde. Het is niets meer dan het vernietigen van de instantie van de PDO klasse.

Voorbeeld 6: Sluiten van een verbinding
<?php
// Maken van verbinding
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');

// Sluiten van verbinding
$db = NULL;
?>
Het handmatig sluiten van een databaseverbinding is in principe niet nodig. Aangezien het bestaan van de verbinding direct afhangt van het bestaan van de instantie van de PDO klasse, zal de verbinding verbroken worden zodra een script eindigt. Het veelvuldig tussendoor sluiten en openen van verbindingen kan tot een zeker performance verlies leiden, zeker als je verbinding maakt met een externe database server.

In de rest van deze tutorial zal ik slechts nog voorbeelden geven van verbindingen met een MySQL database. Dit is immers nog steeds de meest gebruikte database achter websites en de meeste lezers zullen hier ervaring mee hebben.
Pagina 3

Uitvoeren van queries

Nu we weten hoe we verbiding maken met een database, kunnen we verder gaan met het uitvoeren van queries. De PDO klasse heeft hiervoor twee methodes, genaam query() en exec(), beschikbaar.

Er zit een klein verschil in werking van deze twee methodes. De eerste, query(), voert enkel de query uit en geeft een resultaat terug. De tweede daarentegen voert de query uit en geeft daarnaast het aantal aangepaste rijen van een INSERT, UPDATE of DELETE query terug.

Voorbeeld 7: Een SELECT query
<?php
$db = new PDO('mysql:host=localhost;dbname=test','user','password');

$sql = "SELECT naam FROM tabel";
$results = $db->query($sql);

foreach($results as $row)
{
echo $row['naam'].'<br>';
}
?>
Allereerst maken we een nieuwe verbiding door een nieuwe instantie van de PDO klasse aan te maken. Vervolgens voeren we met de query() methode een SQL query uit. De resultaat set kunnen we daarna uitlezen met een foreach loop.

Voorbeeld 8: Een UPDATE query
<?php
$db = new PDO('mysql:host=localhost;dbname=test','user','password');

$sql = "UPDATE tabel SET naam = 'Pieter' WHERE naam = 'Piet'";
$affected_rows = $db->exec($sql);

echo 'Aantal aangepaste rijen: '.$affected_rows;
?>
Nu voeren we de query uit met de exec() methode die als resultaat dus een aantal aangepaste rijen teruggeeft. Op deze manier kun je dus direct controleren of er wel wat gewijzigd is.

Bovenstaande queries zijn uitgevoerd op een MySQL database. Maar zoals ik al eerder vermelde kan deze zelfde manier toegepast worden op andere databases als PostgreSQL of SQLite.

Voorbeeld 9: Een INSERT query
<?php
$db = new PDO('mysql:host=localhost;dbname=test','user','password');

$sql = "
INSERT INTO tabel (naam)
VALUES ('Jan')
";

$results = $db->exec($sql);
$insert_id = $db->lastInsertId();

echo 'ID van laatst ingevoegde record: '.$insert_id;
?>
Vanzelfsprekend ziet een INSERT query er precies hetzelfde uit. Vaak wil je na een INSERT query het id van het laatst ingevoegde record weten. Met PDO kun je dat achterhalen met de lastInsertId() methode zoals te zien is in het voorbeeld.

Natuurlijk moeten we niet vergeten om de in te voegen variabelen te beveilgen om SQL injectie te voorkomen. Waar we normaal altijd mysql_real_escape_string() zouden gebruiken, kunnen we nu de quote() methode van de PDO klasse gebruiken.

Voorbeeld 10: Beveiligen van variabelen
<?php
// Gegevens uit een gepost formulier verwerken
$db = new PDO('mysql:host=localhost;dbname=test','user','password');

$naam = $db->quote($_POST['naam']);
$sql = "
INSERT INTO tabel (naam)
VALUES ('".$naam."')
";

$db->exec($sql);
?>
Pagina 4

Foutafhandeling

In de voorbeelden op de vorige pagina heb ik nog geen enkele vorm van foutafhandeling toegepast, maar natuurlijk mag dat niet vergeten worden. Foutafhandeling in een php script is een van de belangrijkste dingen die er zijn.

PDO biedt drie verschillende manieren van foutafhandeling.

[li]SILENT - Fouten worden onderdrukt en zullen handmatig opgevraagd moeten worden[/li]
[li]WARNING - Gebruikt de error_handler van PHP en geeft een E_WARNING[/li]
[li]EXCEPTION - Gebruikt PDOException klasse[/li]

Van dit lijstje is SILENT de standaard instelling. Dat betekent dat je altijd de code en informatie van de foutmelding met de methodes errorInfo() en errorCode() zult moeten ophalen om te controleren of er fouten opgetreden zijn. Een uitzondering op deze regel is een fout bij het verbinden met de database, oftewel het instantieren van de PDO klasse. In dat geval wordt er een PDOException object aangemaakt dat af te vangen is met een try/catch combinatie.

Voorbeeld 11: Foutmelding van verbinding afvangen
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=bestaat_niet','user','password');
}
catch(PDOException $e)
{
echo $e->getMessage();
}
?>
Dit zal de volgende melding geven:

SQLSTATE[42000] [1049] Unknown database 'bestaat_niet'

Zoals we zien krijgen we netjes een melding dat de opgegevens database niet bestaat.

Het is van belang dat wel altijd op deze manier controleren of een verbinding gelukt is. Mocht er namelijk iets fout gaan en de fout wordt niet afgevangen, dan zal PHP een backtrace weergeven waarin mogelijk alle details van de database verbinding staan. Dit omvat dus mogelijk ook gegevens als gebruikersnaam en wachtwoord van je database!

Als we met de standaard instelling een fout maken in een query die we uitvoeren, krijgen we daar geen foutmelding van te zien.

Voorbeeld 12: Fout in query
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');

$sql = "SELECT naam FROM bestaat_niet";
$results = $db->query($sql);

foreach($results as $row)
{
echo $row['naam'].'<br>';
}
}
catch(PDOException $e)
{
echo $e->getMessage();
}
?>
De enige melding die we nu krijgen is:

Warning: Invalid argument supplied for foreach() in /Users/jorendewit/Sites/jorendewit/phphulp/pdo.php on line 9

We zien dat $results dus geen geldig argument voor de foreach() is en dus doet dat ons vermoeden dat er iets fout gegaan is met de query. Maar als dat zo is, willen we daar eigenlijk gewoon een foutmelding van krijgen.

In dat geval zouden we ervoor kunnen kiezen om de WARNING instelling te gebruiken. In dat geval wordt er naast het aanmaken van een error code ook een standaard E_WARNING melding gegeven.

Het type foutafhandeling kunnen we instellen door het type toe te kennen aan het attribuut dat de foutafhandeling bepaald. Dit doen we met de setAttribute() methode.

Voorbeeld 13: Foutafhandeling met WARNING
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);

$sql = "SELECT naam FROM bestaat_niet";
$results = $db->query($sql);

foreach($results as $row)
{
echo $row['naam'].'<br>';
}
}
catch(PDOException $e)
{
echo $e->getMessage();
}
?>
De foutmelding die nu gegeven wordt:

Warning: PDO::query() [function.PDO-query]: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.bestaat_niet' doesn't exist in /Users/jorendewit/Sites/jorendewit/phphulp/pdo.php on line 8

Warning: Invalid argument supplied for foreach() in /Users/jorendewit/Sites/jorendewit/phphulp/pdo.php on line 10

We zien dat er zowel een foutmelding gegeven wordt op het punt waar de query uitgevoerd wordt als op de plaats van de foreach(). Zoals je altijd te werk gaat met debuggen, los je nu ook eerst de eerste fout op. Dit zal in dit geval ook de tweede foutmelding laten verdwijnen.

Als laatste instelling hebben we EXCEPTION over. In dat geval zal er net zoals in het geval van de fout met de verbinding, voor de fout een PDOException object aangemaakt worden. De gegevens van deze foutmelding kunnen we wederom uitlezen in het catch gedeelte van het script.

Voorbeeld 14: Exception als foutafhandeling
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$sql = "SELECT naam FROM bestaat_niet";
$results = $db->query($sql);

foreach($results as $row)
{
echo $row['naam'].'<br>';
}
}
catch(PDOException $e)
{
echo $e->getMessage();
}
?>
De volgende foutmelding is gegenereerd:

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.bestaat_niet' doesn't exist

We zien dat we hier alleen een foutmelding op de query te zien krijgen en geen verdere meldingen dat bijvoorbeeld het argument van de foreach() niet juist is.

Zoals gewoonlijk kunnen we natuurlijk het hele PDOException object uitkleden en alle informatie over een fout naar boven halen.

Voorbeeld 15: Uitgebreide foutmelding
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$sql = "SELECT naam FROM bestaat_niet";
$results = $db->query($sql);

foreach($results as $row)
{
echo $row['naam'].'<br>';
}
}
catch(PDOException $e)
{
echo '<pre>';
echo 'Regelnummer: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage().'<br>';
echo '</pre>';
}
?>


Regelnummer: 8
Bestand: /Users/jorendewit/Sites/jorendewit/phphulp/pdo.php
Foutmelding: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.bestaat_niet' doesn't exist


Ik zou aanraden om de Exception te gebruiken als foutafhandeling. Je gebruikt de try/catch syntax immers toch al om te controleren of de verbinding gelukt is. Daarnaast krijg je alle opvolgende foutmeldingen die het gevolg zijn van een foute query niet te zien en kun je helemaal zelf bepalen welke informatie je wel wilt tonen.
Pagina 5

Prepared statements

Op dit punt zul je misschien denken dat de PDO extensie niet veel meer biedt dan de mogelijkheid van het verbinden met meerdere database systemen en het uitvoeren van database gerelateerde taken. Dat is zeker niet het geval aangezien PDO met een aantal mooie features komt, waaronder het werken met prepared statements.

Deze prepared statements bieden de programmeur de mogelijkheid tot het creeren van queries die veiliger zijn, waarvan de performance beter is en die eenvoudiger te noteren zijn. Het grote voordeel van prepared statements is dat een bepaalde query op een efficiente manier vele malen achter elkaar uitgevoerd kan worden.

Bij het gebruik van prepared statements wordt er allereerst een template van de uit te voeren query naar de server gestuurd. Deze wordt door de server gecontroleerd op syntax fouten en uitgevoerd om te controleren of de query logisch is. Vervolgens wordt deze opgeslagen in een speciale buffer.

Op het moment dat de query daadwerkelijk uitgevoerd moet worden, stuur je de in de query in te vullen gegevens naar de database. Vervolgens wordt een complete query gevormd en uitgevoerd.

Voorbeeld 16: Uitvoeren van een prepared INSERT statement
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$sql = "
INSERT INTO tabel (naam)
VALUES (:naam)
";

$stmt = $db->prepare($sql);

$stmt->bindParam(':naam', $naam, PDO::PARAM_STR);

$naam = 'Jan';
$stmt->execute();
}
catch(PDOException $e)
{
echo '<pre>';
echo 'Regel: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage();
echo '</pre>';
}
?>
In dit script hebben we eerst een template gemaakt van de INSERT query die we willen gebruiken. Het stukje ':naam' in de template is een zogenaamde parameter marker of placeholder en geeft de plaats aan waar straks een variabele ingevuld gaat worden.

Vervolgens roepen we de prepare() methode aan om de query naar de server te sturen en te laten controleren. Als dat gelukt is, gebruiken we de bindParam() methode. Met deze methode koppelen we een bepaalde variabele aan een bepaalde placeholder. Op die manier weet PHP straks welke variabele hij onder welke naam naar de server moet sturen zodat deze het begrijpt. De optionele derde paramter van deze methode geeft aan als wat voor type de variabele behandeld moet worden, in dit geval dus een string.

Nadat we de variabele vervolgens een waarde hebben gegeven, sturen we alles wederom naar de server met de execute() methode. Als alles goed is gegaan krijgen we geen foutmeldingen en zal er een nieuw record ingevoegd zijn.

Natuurlijk is het ook mogelijk om meerdere placeholders te gebruiken om meerdere gegevens in te voeren. En daarnaast kunnen we ook nog eens meerdere queries achter elkaar uitvoeren.

Voorbeeld 17: Meerdere gegevens en meerdere queries
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);


$personen = array(
array(
'naam' => 'Piet',
'email' => '[email protected]',
'leeftijd' => 34 ),
array(
'naam' => 'Kees',
'email' => '[email protected]',
'leeftijd' => 40 ),
array(
'naam' => 'Karel',
'email' => '[email protected]',
'leeftijd' => 25 )
);

$sql = "
INSERT INTO tabel (naam, leeftijd, email)
VALUES (:naam, :leeftijd, :email)
";

$stmt = $db->prepare($sql);

$stmt->bindParam(':naam', $naam, PDO::PARAM_STR);
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
$stmt->bindParam(':leeftijd', $leeftijd, PDO::PARAM_INT);

foreach($personen as $persoon)
{
$naam = $persoon['naam'];
$email = $persoon['email'];
$leeftijd = $persoon['leeftijd'];
$stmt->execute();
}
}
catch(PDOException $e)
{
echo '<pre>';
echo 'Regel: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage();
echo '</pre>';
}
?>
Voor elke placeholder die we in de query template aangegeven hebben, roepen we de bindParam() methode aan om een variabele te koppelen.

We lezen de array met gegevens uit met een foreach loop, geven de gekoppelde variabelen een waarde en voeren de statement uit met de execute() methode. Je ziet dat ik geen gebruik maak van de quote() methode om in te voegen strings te beveilgen. Dit is niet nodig aangezien de execute() methode al zorgt voor een veilige afhandeling van de gegevens.

Daarnaast is er nog een andere manier waarop we de prepared statement uit kunnen voeren. We kunnen alle gegevens namelijk ook in een array als parameter meegeven aan de execute() methode van het statement.

Voorbeeld 18: Statement uitvoeren met gegevensarray als parameter
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);


$personen = array(
array(
':naam' => 'Piet',
':email' => '[email protected]',
':leeftijd' => 34 ),
array(
':naam' => 'Kees',
':email' => '[email protected]',
':leeftijd' => 40 ),
array(
':naam' => 'Karel',
':email' => '[email protected]',
':leeftijd' => 25 )
);

$sql = "
INSERT INTO tabel (naam, leeftijd, email)
VALUES (:naam, :leeftijd, :email)
";

$stmt = $db->prepare($sql);
foreach($personen as $persoon)
{
$stmt->execute($persoon);
}
}
catch(PDOException $e)
{
echo '<pre>';
echo 'Regel: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage();
echo '</pre>';
}
?>
Zoals je ziet hoe je geen gebruik meer te maken van de bindParam() methode, de array met gegevens in de execute() methode doet zijn werk.

Voorbeeld 19: Updaten van gegevens met een prepared statement
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$personen = array('Piet', 'Karel');

$sql = "
UPDATE
tabel
SET
email = 'geen mail'
WHERE
naam = :naam
";

$stmt = $db->prepare($sql);

$stmt->bindParam(':naam', $naam, PDO::PARAM_STR);

foreach($personen as $persoon)
{
$naam = $persoon;
$stmt->execute();

if($stmt->rowCount() == 0)
{
throw new PDOException('Er zijn geen rijen gewijzigd in de UPDATE query met naam: '.$naam);
}
}
}
catch(PDOException $e)
{
echo '<pre>';
echo 'Regel: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage();
echo '</pre>';
}
?>
Dit voorbeeld lijk veel op het INSERT voorbeeld maar kent toch een belangrijk verschil. Bij een UPDATE of DELETE query is het namelijk altijd van belang om te controleren of er wel degelijk rijen aangepast zijn.

Met de methode rowCount() kunnen we het aantal aangepaste rijen van het laatste uitgevoerde statement ophalen. Mocht dit gelijk zijn aan 0 dan creëren we een nieuw PDOException object met de betreffende melding.

Selecteren van gegevens met een prepared statement
Naast het invoegen, updaten en verwijderen van records met een prepared statement, is het ook mogelijk om een SELECT query uit te voeren.

Hoewel een het uitvoeren van een prepared SELECT statement veel lijkt op de voorbeelden hierboven, werkt het toch net iets anders. We hebben hier namelijk te maken met gegevens die we uit de database ophalen, dus zullen we die gegevens op een bepaalde manier zichtbaar moeten maken.

Voorbeeld 20: Gegevens uitlezen met fetch()
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$sql = "SELECT naam FROM tabel WHERE leeftijd > :leeftijd";

$stmt = $db->prepare($sql);
$stmt->bindParam(':leeftijd', $leeftijd, PDO::PARAM_INT);

$leeftijd = 30;
$stmt->execute();

while($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
echo $row['naam'].'<br>';
}
}
catch(PDOException $e)
{
echo '<pre>';
echo 'Regel: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage();
echo '</pre>';
}
?>
Dit geeft als resultaat iets als:

Piet - 34
Kees - 40

Tot aan de execute() methode is dit script precies hetzelfde als voorgaande voorbeelden. Maar zoals ik al zei moeten we de resultaten van de query natuurlijk zichtbaar kunnen maken. Hier gebruiken we de fetch() methode voor.

Aan de fetch() methode kunnen we optioneel een parameter meegeven om aan te geven hoe we willen dat de rijen gefetched worden. Dit is vergelijkbaar met de optionele parameter van de mysql_fetch_array() functie.

De belangrijkste beschikbare parameters zijn:

[li]PDO::FETCH_ASSOC - Een rij als associtieve array[/li]
[li]PDO::FETCH_NUM - Een rij als numerieke array[/li]
[li]PDO::FETCH_BOTH - Een rij zowel associtieve als numeriek te gebruiken (standaard)[/li]

In tegenstelling tot bijvoorbeeld de MySQLi extensie kent PDO geen methode om het aantal rijen in een resultaatset te bepalen. Wil je dit aantal weten, dan zul je een extra query uit moeten voeren om dat aantal te bepalen. Het makkelijkste is het om hier een query met COUNT() voor te gebruiken:

Voorbeeld 20a: Bepalen van het aantal rijen in een resultaat set

<?php
try 
{
	$sql = "SELECT COUNT(naam) FROM tabel WHERE leeftijd > 30";
	$row = $db->query($sql)->fetch(PDO::FETCH_ASSOC);
	echo $row['aantal']; // Output: 2
}
catch(PDOException $e)
{
    echo '<pre>';
    echo 'Regel: '.$e->getLine().'<br>';
    echo 'Bestand: '.$e->getFile().'<br>';
    echo 'Foutmelding: '.$e->getMessage();
    echo '</pre>';
}
?> 

In dit voorbeeld gebruiken we query() om een eenvoudige query uit te voeren en fetch() om het resultaat van de query in een array te zetten. De variabele $row['aantal'] zal in dit geval het aantal rijen in de resultaat set van de query bevatten.

Verder met het fetchen van gegevens. Een tweede methode die we daarvoor kunnen gebruiken is fetchAll().

Voorbeeld 21: Gegevens uitlezen met fetchAll()
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$sql = "SELECT naam FROM tabel WHERE leeftijd > :leeftijd";

$stmt = $db->prepare($sql);
$stmt->bindParam(':leeftijd', $leeftijd);

$leeftijd = 30;
$stmt->execute();

$result = $stmt->fetchAll();

foreach($result as $row)
{
echo $row['naam'].'<br>';
}
}
catch(PDOException $e)
{
echo '<pre>';
echo 'Regel: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage();
echo '</pre>';
}
?>
Wederom zien we hier hetzelfde resultaat:

Piet - 34
Kees - 40

De fetchAll() methode doet dus niets meer dan alle rijen uit de resultaat set onder een nieuwe key in een array zetten. Vervolgens kunnen we deze array met een foreach loop uitlezen.

Aan de fetchAll() methode kunnen we natuurlijk ook weer een parameter meegeven om aan te geven hoe de resultaten gefetched moeten worden.
Pagina 6

Transacties

Transacties klinken de doorgewinterde databasegebruiker waarschijnlijk zeer bekend in de oren, maar niet iedereen zal weten wat je met dit principe kunt. Simpel gezegd heb je bij transacties in een database de mogelijkheid om een reeks queries uit te voeren zonder dat je eventuele aanpassingen definitief doorvoert. Aan het einde van deze reeks kun je COMMIT gebruiken om de wijzigingen definitief door te voeren of ROLLBACK om alle wijzigingen terug te draaien.

De PDO extensie biedt ons drie methodes voor het werken met transacties: beginTransaction(), commit() en rollBack(). De eerste methode roepen we aan het begin aan, deze zorgt ervoor dat autocommit uitgezet wordt en dat queries pas definitief doorgevoert worden zodra commit() aangeroepen wordt. De rollback() methode kunnen we gebruiken om gemaakte wijzigingen terug te draaien mocht er een fout optreden.

Voorbeeld 22
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$db->beginTransaction();

$db->query("INSERT INTO tabel (naam) VALUES ('Piet')");
$db->query("INSERT INTO tabel (naam) VALUES ('Jan')");
$db->query("INSERT INTO tabel (naam) VALUES ('Karel')");
$db->query("INSERT INTO tabel (naam) VALUES ('Kees')");

$db->commit();

}
catch(PDOException $e)
{
if(isset($db))
{
$db->rollBack();
}

echo '<pre>';
echo 'Regel: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage();
echo '</pre>';
}
?>
We zien hier inderdaad de drie methodes waar ik het al eerder over had terug. Na beginTransaction() voeren we een viertal INSERT queries uit. Als alles goed gaat voeren we de wijzigingen definitief door met commit(). Mocht er een fout optreden dan vangen we die op in het catch gedeelte van het script en draaien we alle reeds gemaakte wijzigingen terug.

We moeten we even controleren of $db bestaat, aangezien dit gedeelte ook fouten in de verbinding afvangt. Als daar een fout in optreedt zal $db niet bestaan en zal het aanroepen van rollback() niet mogelijk zijn.

Hoewel ik hier nu 4 losse queries gebruikt heb, kunnen we deze natuurlijk ook weer vervangen door een prepared statement. Dat zou een nettere oplossing geven.

Een ander goed voorbeeld om het gebruik van transacties te verduidelijken is een geldtranscatie tussen twee bankrekeningen van respectievelijk persoon A en B. Er zal 500 euro overgemaakt worden van persoon A naar persoon B, maar persoon A mag natuurlijk geen negatief saldo krijgen.

Daarnaast kan het totale saldo uit de tabel nooit groter of kleiner worden, er kan immers geen geld ontstaan of verdwijnen. Dat betekent dat de ene query niet uitgevoerd mag worden zonder dat de ander ook uitgevoerd wordt. Duidelijk iets om een transactie voor te gebruiken.

Voorbeeld 23: Geldtransactie tussen twee bankrekeningen
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$queries[] = "UPDATE rekeningen SET saldo = saldo - 500 WHERE klant = 'A'";
$queries[] = "UPDATE rekeningen SET saldo = saldo + 500 WHERE klant = 'B'";
$controle = "SELECT saldo FROM rekeningen WHERE klant = 'A'";

$db->beginTransaction();

foreach($queries as $query)
{
$affected = $db->exec($query);
if($affected == 0)
{
throw new PDOException('Update query niet geslaagd: '.$query);
}
}

$result = $db->query($controle);
$row = $result->fetchAll();

if($row[0]['saldo'] < 0)
{
throw new PDOException('Saldo niet toereikend');
}

$db->commit();


echo '<pre>'.print_r(get_class_methods($db) ,true).'</pre>';

}
catch(PDOException $e)
{
if(isset($db))
{
$db->rollBack();
}

echo '<pre>';
echo 'Regel: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage();
echo '</pre>';
}
?>
We beginnen met het uitvoeren van de UPDATE queries. Mocht daar een fout in optreden, dan willen we dat natuurlijk weten dus maken we in dat geval een nieuw PDOException object aan.

Daarna voeren we de controle query uit en controleren het resultaat. Als dit kleiner is dan 0 heeft persoon A een negatief saldo. Omdat dit niet toegestaan is, gooien we er weer een foutmelding tegenaan zodat alle wijzigingen teruggedraaid worden.

Op deze manier zorgt onze transactie ervoor dat er dus nooit een query zonder de ander uitgevoerd kan worden en dat alles teruggedraaid wordt als er ergens een negatief saldo ontstaat.
Pagina 7

Slotwoord en referenties

Tot zover deze tutorial over de PDO extensie van PHP 5. Ik heb geprobeerd om een uitgebreid overzicht te geven van de mogelijkheden die deze extensie biedt. Hoewel ik veel onderwerpen behandeld heb, heb ik me toch moeten beperken tot de belangrijkste onderwerpen. De extensie biedt dan ook nog veel meer mogelijkheden dan in deze tutorial beschreven. Kijk ook eens op onderstaande links voor meer informatie.

Het grote voordeel van PDO moge nu duidelijk zijn. Het biedt de mogelijkheid om met een universele code te communiceren met verschillende databases. Wissel je van database, dan hoef je enkel nog aan de SQL te sleutelen. De PHP code blijft precies hetzelfde!

Mochten er nog reacties, opmerkingen of vragen zijn over deze tutorial, dan hoor ik dat natuurlijk graag.

Deze tutorial is ook hier te vinden.

Bronvermelding
[li]PDO op PHP.net[/li]
[li]Using PDO Objects in PHP 5[/li]
[li]Introduction to PHP PDO[/li]

Reacties

0
Nog geen reacties.