CRUD in PHP und MySQL mit PDO erstellen

In diesem Artikel möchte ich recht ausführlich beschreiben, wie man mit Hilfe von PDO aus PHP heraus elegant auf eine MySQL-Datenbank zugreift. Dabei steht natürlich wie immer die saubere Trennung der Verantwortlichkeiten und gut les- und wartbarer Code im Vordergrund.

 

CRUD

Unter dem Akronym CRUD werden die wichtigsten SQL-Befehle zur Manipulation von Daten zusammengefasst. Es bedeutet:

  • Create
    Erstellen neuer Datensätze. Erfolgt via SQL-Befehl INSERT
  • Read
    Lesen vorhandener Datensätze aus der Datenbank. Erfolgt via SQL-Befehl SELECT
  • Update
    Aktualisieren vorhandener Datensätze in der Datenbank. Erfolgt via SQL-Befehl UPDATE
  • Delete
    Löschen vorhandener Datensätze aus der Datenbank. Erfolgt via SQL-Befehl DELETE

 

Wichtig: Ich gehe an dieser Stelle davon aus, dass der Umgang mit SQL und den entsprechenden Befehlen bekannt ist. Es gibt einige Quellen im Internet, die hierfür ganz gut geeignet scheinen (etwa auf Lernen²).

 

PDO

Die PHP Data Objects sind seit PHP 5 eine datenbankunabhängige Programmierschnittstelle für den Zugriff auf Datenbanken. Zur Anbindung einer konkreten Datenbank benötigt man einen passenden PDO-Treiber für  diese Datenbank.

Für MySQL reicht es, die folgende Zeile in der Datei php.ini in unserem PHP-Verezichnis zu aktivieren,in dem das führende Semikolon (;) entfernt wird:

 

Datenbankverbindung herstellen

Damit wir unsere Datenbank manipulieren können, müssen wir zuerst eine Verbindung zur Datenbank aufbauen. Das machen wir, indem wir ein neues PDO-Objekt erstellen:

Was benötigen wir, um dieses Objekt zu erstellen?

  1. Den Namen des Hosts, auf dem die Datenbank erreicht werden kann. Typischerweise ist das auf einem Webhost „localhost“.
  2. Den Namen der Datenbank, gegen die wir uns verbinden möchten
  3. Den Datenbank-Benutzer, der Rechte an der Datenbank hat
  4. Das Passwort des Benutzers.

 

Die Verbindung wird über eine sogenannte DataSource über eine Connection-URI angegeben. Der Aufbau dieser ist von Datenbank zu Datenbank unterschiedlich.

 

Ausführen von Statements

Nun, da wir eine Verbindung zu unserer Datenbank haben, können wir auch darauf zugreifen und die ersten CRUD-Statements ausführen.

Als Beispiele verwende ich hier Tabellen aus meiner Kochbuch-Anwendung. Hierbei werden Rezepte (Recipes) und Zutaten (Ingredients) verwaltet.

 

Im Grunde gibt es zwei Arten, auf die wir unsere Statements ausführen können.

 

Einfache Queries

Möchten wir nur einfache Lese-Anweisungen ausführen, ohne dass wir Parameter verwenden, können wir dies ganz einfach dadurch tun, in dem wir ein SQL-Statement über die query-Methode des PDO-Objekts aufrufen:

Auf diese Weise können wir einfache SQL-Statements ausführen. Dies sollten wir aber nur dann verwenden, wenn wir  …

  • … eine READ-Operation durchführen wollen
  • … keine Parameter an das Statement übergeben müssen

 

Prepared Statements

Haben wir hingegen Parameter, die wir an die Datenbank weitergeben möchten oder wollen wir manipulierende Aktionen durchführen (Create, Update, Delete) arbeiten wir sogenannten Prepared Statements.

Die Ausführung eines solchen erfolgt immer in drei Schritten.

  1. Vorbereiten des Statements (prepare)
  2. Bestücken der Parameter mit Werten (bind)
  3. Ausführen des Statements (execute)

Durch das Binden der Parameter wird der Platzhalter in dem Statement (im Beispiel :name) durch den Wert ersetzt. Erst so wird das Statement vollständig und kann ausgeführt werden. Hierbei muss neben dem Namen des Parameters und dem Wert auch der Datentyp angegeben werden. Standardwert ist dabei PDO::PARAM_STR für einen String-Parameter. Weitere mögliche Werte sind:

  • Integer -> PARAM_INT
  • Boolean -> PARAM_BOOL
  • LOB -> PARAM_LOB

 

Das Binding kann auch anonym erfolgen. Dann wird der Platzhalter als ? ausgedrückt. Beim Execute können dann die Parameter gleich als Array mitgegeben werden. Diese müssen dann allerdings in der richtigen Reihenfolge angegeben werden. Hier das gleiche Beispiel nochmal mit anonymen Parametern:

Mir persönlich gefällt das explizite Binden besser, in den folgenden Beispielen werde ich dies verwenden.

 

Wichtig: Um Parameter an SQL-Statements zu übergeben, arbeiten wir nie – niemals – mit aneinandergeketteten Strings. Warum das so ist, wird in der Wikipedia genau erklärt. Siehe dort unter SQL-Injection.

 

Ergebnis auswerten

Nun haben wir unser Statement zusammengebaut und ausgeführt. Als nächstes wollen wir natürlich auch das Ergebnis aus der Datenbank sehen. Dazu bietet uns PDO eine Reihe von fetch-Methoden, mit dessen Hilfe wir das Ergebnis abholen können.

  • fetch(fetch_style)
    Ruft die nächste Zeile aus der Ergebnismenge ab. Über den ersten Parameter können wir bestimmen, wie wir unser Ergebnis aufbereitet haben möchten. Dafür haben wir verschiedene Möglichkeiten. Ich werde hier nur auf einige davon eingehen. Weitere sind in der PHP-Doku zur Methode fetch zu finden.

    • PDO::FETCH_ASSOC
      Gibt ein Assoziatives Array zurück, bei dem der Spaltenname aus der Tabelle der Schlüssel ist
    • PDO::FETCH_NUM
      Gibt ein indiziertes Array zurück, wobei die Spaltennummern dem Index entsprechen. Die erste Spalte hat dabei den Index 0.
    • PDO::FETCH_BOTH (Standardwert)
      Kombiniert PDO::FETCH_ASSOC und PDO::FETCH_NUM. Es können also sowohl die Spaltennamen, als auch eine bei 0 beginnende Spaltennummer als Schlüssel verwendet werden
    • PDO::FETCH_OBJ
      Gibt ein anonymes Objekt zurück, bei dem die Werte in Eigenschaften gespeichert werden, die dem Namen der Spalte entsprechen.
  • fetchAll(fetch_style)
    Ruft alle Zeilen aus der Ergebnismenge ab und liefert diese in einem Array zurück. Über den ersten Parameter können wir dabei erneut bestimmen, wie das Ergebnis aufbereitet werden soll. Die Möglichen Werte sind dabei die gleichen wie beim fetch()
    Abhängig davon wird dann entweder ein mehrdimensionales Array oder ein Array mit Objekte mit den einzelnen Zeilen zurückgegeben.
  • fetchObject(class_name)
    Funktioniert im Grunde genauso wir fetch(PDO::FETCH_OBJ). Allerdings haben wir hier die Möglichkeit, gleich eine Klasse zu übergeben, die dann als Objekt zurückgegeben wird. Das erhöht die Typsicherheit und gefällt mir persönlich sehr gut.
  • fetchColumn(column_number)
    Funktioniert im Grunde wie fetchAll(fetch_style), mit dem Unterschied, dass wir hier nur eine einzige Spalte abrufen. Über den Parameter wird die Spaltennummer angegeben, die wir abrufen möchten. Ohne Parameter wird die erste Spalte in Form eines Array zurückgegeben.

 

Schauen wir doch mal, wie wir unsere Zutat nun abrufen können.

Nun können wir auf die einzelnen Werte unserer Zutat zugreifen:

 

Besser gefällt mir aber wie bereits gesagt der Zugriff über das qualifizierte Objekt. Der Code dazu sieht folgendermaßen aus:

Hinweis: Ich arbeite mit Namespaces, weshalb der Name der Klasse Ingredient mit qualifiziertem Namespace (model) erfolgen muss.

Der Zugriff auf den Wert der Zutat ist nun meiner Ansicht nach deutlich besser, da wir z.B. auf Tippfehler im Spaltennamen hingewiesen werden:

 

Wichtig: Bei fetchObject([Klassenname]) muss der Spaltenname der Tabelle identisch sein mit dem Variablennamen in der Model-Klasse. Dabei kommt es insbesondere auf die Groß-Kleinschreibung an. Heißt die Spalte in der Tabelle z.B. Name, muss auch die Variable $Name heißen. Sie darf nicht $name heißen!

 

Tipps

Zum Abschluss noch ein paar Tipps, die mir beim Arbeiten mit PDOs aufgefallen sind

Zählen von Datensätzen

Zum zählen von Datensätzen wird in SQL count() verwendet. Dabei kommt als Ergebnis genau eine Zeile mit einer Spalte zurück. Auf diese kann man folgendermaßen zugreifen:

 

Indexwert einer Spalte um eins erhöhen

Verwaltet man seinen Schlüssel selbst, also ohne Autoinkrement, ist es erforderlich, dass man den Wert – der typischerweise ein Integer ist – ermittelt und vor dem Einfügen eines neuen Datensatzes um eins erhöht.

Mit Hilfe der SQL-Funktion max() ist dies einfach möglich:

 

Nur einen Datensatz lesen

Möchten wir bei einer bestimmten Abfrage nur ein Ergebnis erhalten, können wir dies entsprechend forcieren, in dem wir LIMIT als Schlüsselwort verwenden. Wollen wir z.B. nur genau eine Zutat abrufen, (weil wir wissen, dass es eben nur einmal „Pfeffer“ gibt), geht das folgendermaßen:

 

Mit Hilfe dieser einfachen Funktionen lassen sich alle möglichen Datenbankanwendungen entwickeln.

 

Posted in PHP

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.