Austausch der Datenbankklasse -> Doctrine

Antworten
Benutzeravatar
shadowcat
Administrator
Beiträge: 5283
Registriert: Di 5. Feb 2013, 10:36
Kontaktdaten:

Austausch der Datenbankklasse -> Doctrine

Beitrag von shadowcat »

Das ist sicherlich in erster Linie für Modulentwickler interessant, aber vielleicht auch für diejenigen, die mitbekommen haben, daß es in WB bei der Migration von 2.8.3 auf 2.8.4 Probleme geben wird, weil die Module angepaßt werden müssen.

In WB wurde die Datenbankklasse neu geschrieben - wobei es mehr danach aussieht, als sei sie hauptsächlich neu benannt worden. Hierbei bestehen zwei größere Baustellen:

* Auch die neue Klasse setzt auf veralteten PHP-Funktionen (mysql_*) auf. (Das soll erst mit dem ersten "Service Pack" für WB 2.8.4 korrigiert werden.)
* Die Globale $database wurde gestrichen. Dies erfordert eine Anpassung in ALLEN Modulen.

Wir haben uns für den Einsatz von Doctrine als Datenbank-Engine entschieden, und zwar für die DBAL-Variante. (Database Abstraction Layer.) Auch das wird in erster Linie nur Entwicklern etwas sagen. ;) Im Kern geht es dabei aber um zwei Dinge:

* Doctrine ist eine sehr gut gepflegte Bibliothek, die in verschiedenen Frameworks - wie etwa Symfony2 - zum Einsatz kommt und mit vielen weiteren Frameworks - etwa Zend - kombiniert werden kann.
* Doctrine ist auf dem neuesten Stand (basierend auf PDO), verwendet also keine veralteten PHP-Funktionen.

Weitere Informationen:
https://github.com/doctrine/dbal
http://www.doctrine-project.org/projects/dbal.html

Die Herausforderung bestand nun darin, den Core auf Doctrine umzustellen, ohne die bisherige API - also class.database.php bzw. die Globale $database - zu zerstören. Hierfür wird mit BC 1.1 ein neuer DB-Helper (CAT_Helper_DB) eingeführt, der die Schnittstelle zu Doctrine herstellt. Die bisherige class.database.php mitsamt ihrer API besteht weiterhin, verwendet intern aber den neuen Helper. Ergebnis: Wir können nun alle Vorteile und Möglichkeiten von Doctrine nutzen, ohne daß alte Module irgendwelche Probleme haben! Gleichzeitig schneiden wir nun auch diesen "alten Zopf" sauber ab. :D

Natürlich gibt es auch einen kleinen Nachteil - class.database.php verwendet CAT_Helper_DB, welche ihrerseits Doctrine verwendet. Das sind also 3 Abstraktionsschichten. Allerdings ist das ein eher akademisches Problem, das in der Praxis nicht spürbar sein wird und aufgrund der vielen Vorteile akzeptabel ist.

Was gewinnen wir dadurch?

Die alte class.database.php war ziemlich einfach gehalten und sicherlich ausreichend für die Bedürfnisse von WB. Allerdings nagelt sie WB auch auf mySQL als Datenbank fest.

Doctrine ist nicht nur ein Wrapper um PDO, was fast automatisch bedeutet, daß die Unterstützung weiterer Datenbankderivate möglich wird. Die Engine hat auch einen "Query Builder", der es ermöglicht, SQL-Statements zu erstellen, die unabhängig von der dahinterliegenden Datenbank und deren SQL-Dialekt sind. Ein Beispiel: MSSQL (Microsoft) kennt keine "LIMIT"-Anweisung, mit der man bei mySQL die Anzahl (und auch den eventuellen Offset) der zurückzuliefernden Ergebnisse einer Abfrage einschränken kann. Formuliert man nun ein Statement wie "SELECT * FROM `whatever` LIMIT 1", wird dieses in mySQL funktionieren - in MSSQL aber nicht.

Der Query Builder hingegen bastelt aus der Anweisung, die man ihm gibt, das zur Datenbank passende SQL-Statement zusammen. Beispiel (aus der Doctrine Doku entnommen):

Code: Alles auswählen

$queryBuilder
    ->select('u.id', 'u.name')
    ->from('users', 'u')
    ->setFirstResult(10)
    ->setMaxResults(20);
Das entspricht in mySQL-Dialekt
SELECT `u.id`, `u.name` FROM `users` AS u LIMIT 10,30

Indem wir also mittel- bis langfristig Gebrauch von den neuen Möglichkeiten machen, die uns Doctrine eröffnet, steht auch einer Ausweitung der Datenbankunterstützung auf weitere Derivate nichts mehr im Wege. Zudem muß auch ein Modulentwickler nicht mehr so genau wissen, wie ein Statement exakt lautet - er kann es dem Query Builder überlassen. :D

http://docs.doctrine-project.org/projec ... ilder.html

Und was mache ich jetzt damit?

Das finde ich besonders cool. :D An den Query Builder kommt man nämlich sogar über die gute alte Globale $database dran - auch wenn das natürlich nicht der empfohlene Weg ist.

Code: Alles auswählen

// Variante 1: Über $database
$qb = $database->conn()->createQueryBuilder();
// Variante 2: Über den Helper (empfohlen)
$qb = CAT_Helper_DB::getInstance()->conn()->createQueryBuilder();
Edit: Jetzt auch in kürzer:

Code: Alles auswählen

$qb = $database->qb();
Kurzum: Alles, was die gute alte class.database.php konnte, funktioniert unverändert - auch wenn dahinter jetzt die mächtige Doctrine Engine steckt. Zusätzlich gewinnen wir all die vielen Möglichkeiten, die Doctrine bietet.

Übrigens kann Doctrine bei Bedarf auch direkt aus dem Modulverzeichnis lib_doctrine eingebunden werden. Falls jemand keine Umwege mag. ;)
My software never has bugs, it just develops random features.
If it’s not broken, keep fixing it until it is
Benutzeravatar
shadowcat
Administrator
Beiträge: 5283
Registriert: Di 5. Feb 2013, 10:36
Kontaktdaten:

Re: Austausch der Datenbankklasse -> Doctrine

Beitrag von shadowcat »

Das vermaledeite Prefix

Auch ein schönes Thema: Wie baue ich eine SQL-Abfrage "richtig"?

Gängige Varianten:

Code: Alles auswählen

$database->query("SELECT * FROM ".CAT_TABLE_PREFIX."whatever ...");
$database->query(sprintf("SELECT * FROM %swhatever ...",CAT_TABLE_PREFIX));
Beide Varianten gibt es dann noch "korrekter" mit `` um die Tabellennamen. Schön ist aber beides nicht.

Der neue Datenbankhelper - und damit auch $database - versteht jetzt das hier:

Code: Alles auswählen

$database->query("SELECT * FROM `:prefix:whatever` ...");
Außerdem können wir SQL-Injection-sichere Statements, ENTWEDER direkt über query():

Code: Alles auswählen

$database->query("SELECT * FROM `:prefix:whatever` WHERE `mycol`=:id", array('id'=>$id));
...ODER "traditionell" mit prepare(), bind() und execute().
My software never has bugs, it just develops random features.
If it’s not broken, keep fixing it until it is
Antworten