Active Record Pattern in PHP

StorageIn PHP gibt es verschiedene Implementierungen des Active Record Patterns. Auch wenn es uns nimmer so auffällt, die objektorientierte Welt des Codes und die relationale Welt der Daten sind zwei verschiedene Paradigmen, und es braucht unheimlich viel Leim um sie zusammenzukleben. Objektorientierte Datenbanken entwickeln sich, sind jedoch noch nicht Standard (um es mal vorsichtig auszudrücken). Das Active Record Pattern hilft uns mit ein Bisschen Standard-Klebe.

Mit diesem Pattern können Daten aus der Datenbank gesucht und direkt in Objekte übergeführt werden. Änderungen an den Objekten und das Speichern bewirken, dass diese Änderungen auch in die Datenbank übernommen werden.

Ich habe mich für MyActiveRecord von Jake Grimley entschieden, weil es sauber implementiert, nicht zu gross und trotzdem nicht featurearm ist. Die Dokumentation ist ziemlich gut und reicht aus um zu starten.

Benutzung von MyActiveRecord

Suchen und finden

Der Dreh- und Angelpunkt dürfte die Funktion FindAll($class, $where=0, $orderby='id ASC', $limit=10000, $offset=0) sein. Sie liefert eine Kollektion von Objekten der entsprechenden Klasse zurück:

foreach (MyActiveRecord :: FindAll('thing') as $thing) {
  echo "I am a {$thing->name}!<br/>\n";
}


Alle Tabellenfelder sind bei den zurückgegebenen Objekten als Attribute festgelegt und ermöglichen so einen einfachen Zugriff. Um SQL-Injections zu vermeiden sollte immer Prepare verwendet werden wenn eine böse Aussenvariable verwendet wird.

foreach (MyActiveRecord :: FindAll('thing', myActiveRecord :: Prepare('age>=%d', strtolower($_POST["age"]))) as $thing) {
  echo "I am a {$thing->name}!<br/>\n";
}

Einzelne Strings können auch mit der Funktion Escape geschützt werden.

Erstellen und Ändern

Um ein neues Objekt anzulegen reicht es alle Attribute zu füllen und save aufzurufen:

$thing= MyActiveRecord :: Create('thing', array (
  'name' => 'Little green something',
  ));
$thing->save();

Jede Tabelle muss das Attribut id als eindeutigen Schlüssel besitzen. Hat man ein Objekt geFindAllt, dann ist $thing->id gesetzt. Zum Ändern einfach die Attribute ändern und save()n… So einfach kanns gehen…

Verbindungen

1:n Verbindungen benötigen ein Verbindungsattribut auf der n-Seite; 1 Mutter hat viele Kinder bedeutet, dass die Kindertabelle ein Attribut mit dem (und genau dem) Namen mutter_id enthalten muss, das auf die Muttertabelle referenziert. Gefunden werden können sie mit find_children bzw. find_parent. add_child fügt Kinder hinzu.

m:n Verbindungen benötigen die obligate Zwischentabelle. attach hängt ein Objekt an ein Anderes, link($objekt1, $objekt2) verbindet zwei Objekte. So verlinkte Objekte können mit find_attached bzw. find_linked gesucht und gefunden werden.

SQL

Auch direkt mit SQL kann diese Klasse umgehen: Query($sql) setzt direkt SQL ab. Ebenso kann man die Mächtigkeit von SQL beim Suchen und Finden nutzen: FindBySql.

Weiteres

Toolfunktionen wie TableExists, FindFirst, … runden den Umfang des Paketes ab.

Probleme und Fallen

Wichtig ist, dass alle wichtigen Dinge wie Tabellennamen, Attribute, … klein geschrieben werden.

Klassen zu den Tabellen sollten erst nach dem Erstellen der Tabellen deklariert werden, da es sonst ein Gnusch gibt. Es empfiehlt sich im Sinne der Reinlichkeit bei m:n Beziehungen die Zwischentabelle zu bereinigen wenn ein Objekt gelöscht wird:

// Create mapping classes
class car extends MyActiveRecord
{
    function destroy()
    {
        // clean up attached People (drivers) on destroy
        foreach( $this->find_attached('driver') as $driver ) $this->detach($driver);
        return parent::destroy();
    }  
}

Die Beispieldatei example.php läuft so nicht. Die entsprechenden Tabellennamen müssen zuerst klein geschrieben werden. Eine bereinigte, lauffähige Version kann heruntergeladen werden. Zum Verständnis kann vielleicht noch die Info beitragen, dass die 1:n Beziehung von Auto zum Fahrer den owner des Wagens darstellt, während die m:n Beziehung für die Fahrer steht.

Erweitertes

Folgende Konstanten sind interessant:

  • Wird in MYACTIVERECORD_LOG_SQL_TO ein Dateinamen gesetzt, so werden alle SQL-Statements in die Datei geloggt.
  • Wird MYACTIVERECORD_CACHE_SQL gesetzt, so werden Resultate zu Find Operationen während der Ausführungszeit des Scripts gecached.

Will man bei Prepare das Prozentzeichen verwenden (bspw. für LIKE) muss man es verdoppeln:

foreach (MyActiveRecord :: FindAll('thing', MyActiveRecord :: Prepare("name LIKE  '%%%s%%'", $_POST['searchthing'])) as $thing) {
...

verwendet folgendes SQL-Statement:

SELECT * FROM thing WHERE name LIKE '%gnabbergnuelz%';

Fazit

MyActiveRecord ist ein schnuckeliges Tool, an da man sich gewöhnen könnte. Eine kleine Anwendung, ohne Garantie und Support gibts ebenfalls auf diesem Blog.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.