Datenbankklasse programmieren
Tutorials | letzte Änderung am 09. August '10 um 21:51 Uhr
Motivation
Die Fehlerbehandlung wird um einiges einfacher, wenn wir sämtliche Datenbankfunktionen in einer Klasse kapseln.
Durch Exceptions haben wir eine Möglichkeit, Fehlermeldungen an geeigneter Stelle zu behandeln.
Die Datenbankklasse
Bei folgendem Code handelt es sich um eine sehr einfache Datenbankklasse, die aber durchaus ihren Zweck erfüllt. Bei auftretenden Fehlern werden Ausnahmen geworfen, die wir abfangen und auswerten können. Der Besucher unserer Website wird dadurch nicht durch unschöne Fehlermeldungen vergrault.
<?php
class DatabaseException extends Exception {}
class Database {
private $c = null;
public function __construct() {}
/**
*
* connects to the database
*
* @return Resource
*/
public function connect($host, $username, $password, $database) {
$this->c = mysql_connect($host, $username, $password);
if ($this->c === false) {
throw new DatabaseException('Verbindung zur Datenbank fehlgeschlagen.');
}
if (mysql_select_db($database, $this->c) === false) {
throw new DatabaseException('Die ausgewählte Datenbank existiert nicht.');
}
// set charset
mysql_set_charset('utf8', $this->c);
}
/**
*
* returns the resource ID of the database connection
*
* @return Resource
*/
public function getResourceId() {
return $this->c;
}
public function query($query) {
$result = mysql_query($query, $this->c);
if ($result === false) {
throw new DatabaseException('Die Datenbankabfrage konnte nicht ausgeführt
werden, da sie Fehler enthält: ' . $query);
}
return new Statement($result);
}
public function close() {
mysql_close($this->c);
}
}
Was jetzt noch fehlt, ist eine Klasse für ein Resource-Objekt, dass nach dem Senden einer Datenbankabfrage, mit Hilfe der von mysql_query() zurückgegebenen ResourceID, erzeugt wird und mit dem wir später die selektierten Informationen auslesen können.
Die Klasse Statement sollte am besten in der selben Datei definiert werden, wie die Datenbankklasse, da diese sowieso auf sie zugreifen muss.
<?php
class Statement {
private $resource = null;
public function __construct($resource) {
$this->resource = $resource;
}
/**
*
* equivalent to mysql_fetch_assoc()
*
* @return array
*/
public function fetchAssoc() {
if (is_resource($this->resource)) {
return mysql_fetch_assoc($this->resource);
}
return false;
}
/**
*
* equivalent to mysql_num_rows()
*
* @return int
*/
public function numRows() {
if (is_resource($this->resource)) {
return mysql_num_rows($this->resource);
}
return false;
}
}
In dieser Klasse sind bereits die Funktionen fetchAssoc() und numRows() definiert, die äquivalent zu den MySQL-Funktionen mysql_fetch_array() bzw. mysql_num_rows() sind, jedoch über unser Resource-Objekt angesprochen werden können.
Der Verbindungsaufbau
Der Verbindungsaufbau gestaltet sich sehr einfach: Zunächst wird natürlich ein Datenbankobjekt erzeugt, über das wir die Verbindung aufbauen und später Datenbankabfragen ausführen werden.
<?php
// include the database class
require_once 'database.class.php';
$db = new Database();
// connect
try {
$db->connect('localhost', 'username', 'passwort', 'datenbank');
} catch (DatabaseException $e) {
echo 'Datenbankfehler: ' . $e->getMessage();
}
An dieser Stelle kommen die Ausnahmen (engl. = Exceptions) ins Spiel. Sollte ein Fehler beim Verbindungsaufbau auftreten, wird dieser abgefangen und es kann eine Fehlermeldung ausgegeben werden. Die Fehlermeldung kann an dieser Stelle aber auch genauso gut, für den Besucher unsichtbar, in einer Logdatei gespeichert werden, damit wir die Fehler später auswerten und beheben können.
Eine Datenbankabfrage
<?php
// test database class
try {
$stmt = $db->query("SELECT `name` FROM `testtabelle`");
while ($row = $stmt->fetchAssoc()) {
echo '<p>' . $row['name'] . '</p>';
}
} catch (DatabaseException $e) {
echo 'Es ist ein Datenbankfehler aufgetreten: ' . $e->getMessage();
}
Auch hier können die Exceptions gefangen und beliebig weiterverarbeitet werden.
Antworten
Danke für deinen Hinweis, habe es mal angepasst!
Auf meinem alten kostenlosen Server (funpic.de) wurde mysqli nicht unterstützt und es blieb mir nichts anders übrig, als die alten MySQL-Funktionen zu nutzen.
Die Klasse sollte mir hauptsächlich die Fehlerbehandlung vereinfachen.
Natürlich sind die Möglichkeiten von PDO und die der mysqli - Erweiterung den MySQL-Funktionen vorzuziehen, doch soll es hier nur um einen simplen Ansatz für eine Datenbankklasse gehen, die für Anfänger interessant sein kann, da ihnen nicht immer klar ist, wie so eine Klasse grundsätzlich aussehen kann.
Ich hab da mal ein paar Fragen zu deiner Klasse, ansonsten ist es ganz toll.
Wieso hast du den Verbindungsaufbau zur Datenbank in einer extra Funktion. Das könnte man doch auch gleich im Konstruktor machen, oder. Und bei der Funktion close(), sei mir bitte nicht böse, wenn du schon den Konstruktor einsetzt dann auch den Destruktor.
Für was ist die Funktion getResourceId(), im weiteren Verlauf taucht dieses nicht mehr auf. Auch nicht im Beispielcode wie man die Klasse anwendet.
Und dann noch zu den Variablen ($host, $username, $password, $database), müssen diese nicht oberhalb der Klasse mit private, protected oder public gesetzt werden.
Ansonsten tolle Seite, mach weiter so. Solche kleinen Tutorial werden immer gebracht.
Hi,
danke für deinen Kommentar.
Quote:
Wieso hast du den Verbindungsaufbau zur Datenbank in einer extra Funktion. Das könnte man doch auch gleich im Konstruktor machen, oder.
Klar, so kann man es auch machen, mir fällt auch kein Grund ein, es nicht so zu machen. Ist wohl Geschmacksache!
Quote:
Und bei der Funktion close(), sei mir bitte nicht böse, wenn du schon den Konstruktor einsetzt dann auch den Destruktor.
Der Garbage Collector räumt am Ende sowieso auf. die Funktion close() wird eigentlich nur benötigt, wenn man explizit die Verbindung beenden möchte.
Quote:
Für was ist die Funktion getResourceId(), im weiteren Verlauf taucht dieses nicht mehr auf. Auch nicht im Beispielcode wie man die Klasse anwendet.
Stimmt, ein Beispiel könnte sein:
<?php
// test database class
try {
$stmt = $db->query("SELECT `name` FROM `testtabelle`");
while ($row = $stmt->fetchAssoc()) {
echo '<p>' . $row['name'] . '</p>';
}
} catch (DatabaseException $e) {
echo '[SQL Error] ' . mysql_error($db->getResourceId());
}
Oder du benötigst die Resource-ID beim Zusammenbasteln einer Datenbankabfrage.
mysql_real_escape_string($string, $db->getResourceId())
Quote:
Und dann noch zu den Variablen ($host, $username, $password, $database), müssen diese nicht oberhalb der Klasse mit private, protected oder public gesetzt werden.
Nein, die werden der Funktion ja als Parameter übergeben und werden später nicht mehr benötigt. Man könnte an dieser Stelle über eine Vorbelegung der Parameter mit Standardwerten, wie ('localhost', 'root', '', '...') diskutieren. Ist aber auch Geschmacksache und ändert nichts an der Funktionsweise der Klasse.
Quote:
Ansonsten tolle Seite, mach weiter so. Solche kleinen Tutorial werden immer gebracht.
Danke.
Mir fällt gerade ein, dass es Argumente gegen das Werfen von Exceptions im Konstruktor gibt. Zumindest existieren diese für die Sprache C++ und solange ich nicht weiß, inwiefern dies auch auf PHP zutrifft, belasse ich den Verbindungsaufbau zur Datenbank in einer eigenen Funktion ;)
Das bedeutet dann eben eine Zeile mehr nach der Instanzierung! Damit kann ich leben.
Gruß
Daniel Sentker | verfasst am 28. Juli '10 um 11:17 Uhr
#1
In deinen Kommentaren steht: "equal to ..."
Bedeutet, dass etwas jmd. gewachsen ist - nimm lieber "equivalent to..."
Ich verstehe deinen Ansatz allerdings nicht. Warum erfindest du das Rad neu, wenn mysqli() eine noch ausführlichere Ausarbeitung beinhaltet? Darüber hinaus finde ich die Nutzung von PDO viel flexibler.