7.3 Konfiguration 

Bevor eine Modell-Klasse mit Hilfe von PersistentObject in der Datenbank verwaltet werden kann, muss der Komponente mitgeteilt werden, welche PHP-Klassen persistente Objekte darstellen. Es muss definiert werden, welche Klasse zu welcher Datenbanktabelle gehört und wie die Attribute der Klasse in der entsprechenden Tabelle gespeichert werden sollen. Zusätzlich ist es möglich, Beziehungen zwischen Objekten festzulegen und zu definieren, wie diese Beziehungen in der Datenbank abgebildet sind.
7.3.1 Basiskonfiguration 

Zunächst wird für jede PHP-Klasse, welche persistente Objekte repräsentiert, eine Konfigurationsdatei angelegt. Exemplarisch sehen Sie an dieser Stelle die Konfiguration zur gpBlogEntry-Klasse.
$def = new ezcPersistentObjectDefinition(); $def->table = 'entry'; $def->class = 'gpBlogEntry'; $def->idProperty = new ezcPersistentObjectIdProperty(); $def->idProperty->columnName = 'id'; $def->idProperty->propertyName = 'id'; $def->idProperty->generator =ð new ezcPersistentGeneratorDefinition( 'ezcPersistentNativeGenerator' ); $def->properties['body'] = new ezcPersistentObjectProperty(); $def->properties['body']->columnName = 'body'; $def->properties['body']->propertyName = 'body'; $def->properties['body']->propertyType =
ð ezcPersistentObjectProperty::PHP_TYPE_STRING; $def->properties['date'] = new ezcPersistentObjectProperty(); $def->properties['date']->columnName = 'date'; $def->properties['date']->propertyName = 'date'; $def->properties['date']->propertyType =
ð ezcPersistentObjectProperty::PHP_TYPE_INT; $def->properties['title'] = new ezcPersistentObjectProperty(); $def->properties['title']->columnName = 'title'; $def->properties['title']->propertyName = 'title'; $def->properties['title']->propertyType =
ð ezcPersistentObjectProperty::PHP_TYPE_STRING; return $def;
Listing 7.2 Basiskonfiguration für das Blog-Eintrag-Modell
Die Konfiguration persistenter Objekte erfolgt ebenfalls in PHP-Code. Auf diese Weise ist es möglich, dass in Zukunft verschiedene Dateiformate in eZ Components angeboten werden, um solche Definitionen zu speichern. All diese Formate können dann in PHP-Code kompiliert werden, was den schnellen Zugriff auf benötigte Daten garantiert.
Ein einzelnes Objekt wird über eine Instanz der Klasse ezcPersistentObjectDefinition konfiguriert, welche als wesentlichen Bestandteil die Datenbanktabelle und den Namen der passenden PHP-Klasse enthält. Weiterhin enthält die Definition ein Array, in dem die Attribute, hier $properties genannt, der Klasse und deren Mapping auf eine Datenbankspalte konfiguriert werden. Normale Attribute werden mit Hilfe der Klasse ezcPersistentObjectProperty dargestellt und enthalten jeweils den Namen der Datenbankspalte, $columnName, und den Namen des dazugehörigen Objekt-Attributs als $propertyName. Daneben sollte der PHP-Datentyp angegeben werden, den das Attribut besitzt. Aus Datenbanken gelesene Werte haben in PHP zumeist den Datentyp string, auch wenn es sich eigentlich um Werte vom Typ integer oder float handelt. Setzen Sie $propertyType korrekt, so übernimmt PersistentObject die Typumwandlung nach dem Auslesen aus der Datenbank automatisch. Neben PHP_TYPE_STRING, dem Standardwert, und PHP_TYPE_INT existiert auch noch PHP_TYPE_FLOAT für Fließkomma-Werte.
Eine besondere Funktion übernimmt die sogenannte $idProperty, mit deren Hilfe ein Objekt in der Datenbank identifiziert wird. Es handelt sich also um den Primärschlüssel der Datenbanktabelle. PersistentObject unterstützt zum Entstehungszeitpunkt dieses Buches nur Primärschlüssel, die aus einer einzelnen Spalte bestehen. Wie normale Property-Definitionen verbindet die ID-Property eine Datenbankspalte mit einem Objekt-Attribut. Allerdings besitzt die Klasse ezcPersistentObjectIdProperty zusätzlich das Konfigurationsattribut $generator, dem eine Instanz von ezcPersistentGeneratorDefinition zugewiesen wird. Die hier im Konstruktor übergebene Generator-Klasse übernimmt bei der Speicherung neuer Objekte die Erzeugung eines neuen ID-Werts. Im GP-Blog verwenden wir die Klasse ezcPersistentNativeGenerator, die in Datenbanksystemen wie MySQL und SQLite die integrierte auto_increment-Funktionalität verwendet. Für Datenbanken mit Unterstützung von Sequenzen existiert entsprechend der ezcPersistentSequenceGenerator, der als Parameter den Namen der zu verwendenden Sequenz erwartet. Zur Verwendung von beispielsweise Oracle müssten Sie die Generator-Definition wie folgt anpassen:
$def->idProperty->generator = new
ezcPersistentGeneratorDefinition(
'ezcPersistentSequenceGenerator',
array( 'sequence' => 'entry_id_seq' )
);Listing 7.3 ID-Generator für Sequenzen
Nun würde die Sequenz namens entry_id_seq zur Generierung neuer IDs verwendet. Sowohl der Sequenz-Generator als auch der native Generator tragen dafür Sorge, dass die zugrunde liegende Datenbank eine neue ID für den Primärschlüssel generiert und weisen diese ID anschließend der definierten ID-Property zu. Nach der initialen Speicherung besitzt das persistente Objekt also eine ID, welche Sie entsprechend über das ID-Attribut auslesen, aber, wie im Quellcode der Eintragsklasse exemplarisch gesehen, nicht ändern können.
Neben diesen beiden Generatoren steht in eZ Components noch der ezcPersistentManualGenerator zur Verfügung. Falls Sie diesen Generator verwenden, kümmert sich PersistentObject nicht um die Generierung einer ID; Sie müssen manuell dafür sorgen. Mit Hilfe dieses Generators ist es zum Beispiel möglich, Felder vom Datentyp string als ID-Property zu verwenden.
PersistentObject stellt Ihnen frei, eigene Generatoren zu implementieren, wozu Sie die abstrakte Basisklasse ezcPersistentIdentifierGenerator erweitern. Die zu implementierenden Methoden für einen eigenen Generator haben die folgende Signatur:
abstract public function preSave(
ezcPersistentObjectDefinition $def,
ezcDbHandler $db,
ezcQueryInsert $q
);
abstract public function postSave(
ezcPersistentObjectDefinition $def,
ezcDbHandler $db
);Listing 7.4 Signatur von ezcPersistentIdentifierGenerator
Die Methode preSave() wird, wie der Name bereits andeutet, vor der Speicherung eines Objekts aufgerufen. Sie erhält als Parameter das Definitionsobjekt, zu dem eine ID erzeugt werden soll sowie die verwendete Datenbankverbindung und das Query-Objekt, das zum Einfügen in die Datenbank benutzt wird. Aufgabe dieser Methode ist, gegebenenfalls die INSERT-Query zu manipulieren, sodass der neue Wert entsprechend generiert wird. Alle benötigten Informationen, zum Beispiel über den zu verwendenden Spalten-Namen, erhalten Sie über das Definitionsobjekt. Das Datenbankobjekt gibt Aufschluss über die verwendete Datenbank, was die Beachtung spezifischer Eigenheiten des Systems ermöglicht.
Nach dem Einfügen des Objekts in die Datenbank wird die Methode postSave() auf dem Generator aufgerufen. Dies ist notwendig, um zum Beispiel die zuletzt vergebene ID zu ermitteln. Wird aus dieser Methode ein anderer Wert als null zurückgegeben, weist PersistentObject ihn entsprechend als ID-Wert dem PHP-Objekt zu.
Zusätzlich zu diesen abstrakten Methoden enthält die Basisklasse noch die Methode checkPersistence():
public function checkPersistence(
ezcPersistentObjectDefinition $def,
ezcDbHandler $db,
array $state
)Listing 7.5 Die checkPersistence()-Methode der Generator-Basisklasse
Diese können Sie bei Bedarf überschreiben, denn mit ihrer Hilfe testet Persistent Object, ob ein Objekt bereits in der Datenbank gespeichert oder neu ist. Je nach Status des Objekts funktioniert entweder eine INSERT- oder UPDATE-Anfrage. Gibt die Methode false zurück, handelt es sich um ein neues Objekt, bei true ist das Objekt bereits in der Datenbank gespeichert. Die Basisimplementierung testet, ob die ID-Property den Typ null hat, also noch nicht gesetzt ist. Bei Bedarf können Sie diese Methode ebenfalls überschreiben und somit Ihren Bedürfnissen anpassen.
Die Basiskonfigurationen der beiden weiteren Modellklassen gpBlogComment und gpBlogTag sind völlig analog gestaltet, weshalb der Sourcecode hier nicht abgedruckt wird. Sie können ihn jedoch auf der beiliegenden CD im Verzeichnis stage05/ einsehen. Die Konfigurationsdateien müssen an einem für die Komponente PersistentObject auffindbaren Ort gespeichert werden und einem bestimmten Namensschema folgen. Zu diesem Zweck haben wir bereits in Kapitel 3, »Die Applikationsbasis«, das Verzeichnis persistent/ erzeugt. Zur Benennung der Dateien muss der Name der entsprechenden Klasse verwendet werden, allerdings komplett in Kleinbuchstaben. Als Dateiendung kommt wie gewohnt .php zum Einsatz. Die zuvor gezeigte Konfiguration für die Eintragsklasse wird also im Unterverzeichnis persistent/gpblogentry.php abgelegt.
| Konfiguration zurückgeben |
|
Am Ende der Konfigurationsdatei befindet sich, wie im Beispielcode dargestellt, ein return-Statement, welches die Konfiguration beim Inkludieren der Datei an das include- beziehungsweise require-Statement zurückgibt. Diesen Mechanismus verwendet PersistentObject, um die Konfiguration zu lesen. Sollten Sie dieses Statement vergessen, wird PersistentObject es Ihnen mit einer ezcPersistentDefinitionNotFoundException danken. Achten Sie also darauf und prüfen Sie gegebenenfalls, falls Sie eine solche Exception erhalten. Hierbei handelt es sich um eine häufige Ursache für eine Exception, neben einer Fehlkonfiguration von PersistentObject selbst. Näheres dazu erfahren Sie in Abschnitt 7.4.1, »Eine Persistenz-Sitzung erzeugen«. |
7.3.2 Relationen beschreiben 

Nachdem Sie nun gesehen haben, wie ein persistentes Objekt grundlegend konfiguriert wird, folgt in diesem Abschnitt die Definition von Beziehungen zwischen Objekten. Die bisher gezeigte Konfiguration reicht bereits aus, um mit Hilfe von PersistentObject PHP-Objekte zu laden, zu speichern, zu löschen und in der Datenbank aufzufinden. Während diese Standardkonfiguration für alle Modelle des GP-Blogs sehr ähnlich aussieht, ist die Konfiguration der Relationen jeweils unterschiedlich, weshalb Sie nun mehrere Varianten kennenlernen werden.
Zunächst wird die Relationen-Konfiguration für die Klasse gpBlogEntry vorgestellt. Der Definitionscode für die Verbindungen wird in die im letzten Abschnitt vorgestellte Konfigurationsdatei mit eingefügt. Bei der Variablen $def handelt es sich also um die Instanz von ezcPersistentObjectDefinition, die zuvor erzeugt wurde. Der im Folgenden gezeigten Code ist jeweils vor der Rückgabe von $def aus der Datei einzufügen.
$def->relations['gpBlogComment'] = newð ezcPersistentOneToManyRelation( "entry", "comment" ); $def->relations['gpBlogComment']->columnMap = array( new ezcPersistentSingleTableMap( "id", "entry_id" ), ); $def->relations['gpBlogComment']->cascade = true;
Listing 7.6 1:n-Relationen konfigurieren
In diesem Codeabschnitt wird zunächst eine Verbindung zur Klasse gpBlogComment definiert, was über das Attribut $relations des ezcPersistentObjectDefinition-Objekts geschieht. Bei diesem Attribut handelt es sich um ein Array, oder besser gesagt um ein Objekt, welches ein Array simuliert, dessen Schlüssel den Namen der verbundenen Klasse darstellt. PersistentObject unterstützt vier Typen von Verbindungen, mit denen sich alle benötigten Datenbankrelationen in PHP-Code abbilden lassen. Im dargestellten Fall handelt es sich um eine 1:n-Relation, wie der Name der Relationen-Klasse bereits andeutet. Die Parameter des Konstruktors von ezcPersistentOneToManyRelation bestimmen, welche Tabellen mit Hilfe dieser Relation verbunden werden.
Eine wesentliche Information für die Komponente wird im Attribut $columnMap des Relationen-Objekts abgelegt: Die Spalten, über welche die Verbindung zwischen den Tabellen hergestellt wird. Hier ist es die Spalte id der Tabelle entry, welche sich auf die Spalte entry_id der Tabelle comment bezieht. Im Endeffekt vollziehen Sie an dieser Stelle eine Primärschlüssel-Fremdschlüssel-Beziehung der Datenbank nach, auch wenn einige Datenbanksysteme diesen Mechanismus teilweise nicht unterstützen. Die Benutzung der Klasse ezcPersistentSingleTableMap beschreibt, dass es sich um ein Mapping über nur zwei Tabellen handelt. Im nächsten Beispiel zum Thema n:m-Relation werden Sie die zweite mögliche Klasse für dieses Mapping kennenlernen. Für 1:n-Beziehungen sind immer die ezcPersistentSingleTableMap-Instanzen zu verwenden. Bedingt dadurch, dass es sich bei $columnMap um ein Array handelt, ist es möglich, dass Sie mehrere Spalten-Mappings angeben, statt wie hier gezeigt nur ein einzelnes. So können Sie zwischen mehreren Spalten zweier Tabellen mappen.
Zuletzt wird der Relation eine Option hinzugefügt, welche die Kaskadierung von Löschoperationen veranlasst. Einige Datenbanksysteme unterstützen diesen Mechanismus von sich aus, indem die Kaskadierung beim Erzeugen der Tabellen definiert wird. Ist dies für Ihre Datenbank der Fall, benötigen Sie diese Option nicht. Kaskadierung bedeutet, dass alle verbundenen Datensätze beim Löschen des Ursprungsobjekts automatisch mit gelöscht werden. Natürlich erfolgt das Löschen der verbundenen Objekte in korrekter Reihenfolge, sodass die referenzielle Integrität gewährleistet bleibt. Allerdings ist der in ein Datenbanksystem integrierte Mechanismus mit hoher Wahrscheinlichkeit wesentlich schneller als die von PersistentObject verwendete Implementierung. Sollten Sie also nicht unabhängig von der zugrunde liegenden Datenbank entwickeln müssen, und unterstützt das von Ihnen verwendete Datenbanksystem Kaskadierung, sollten sie diesen Mechanismus verwenden.
Einem Blog-Eintrag können ein oder mehrere Tags zugeordnet werden, wobei ein Tag zu einem oder zu mehreren Blog-Einträgen gehören kann. Aus diesem Grund wird zwischen gpBlogEntry und gpBlogTag eine n:m-Relation definiert.
$def->relations['gpBlogTag'] = new ezcPersistentManyToManyRelation(
"entry",
"tag",
"entry_tag"
);
$def->relations['gpBlogTag']->columnMap = array(
new ezcPersistentDoubleTableMap(
"id",
"entry_id",
"tag_id",
"id"
),
);Listing 7.7 n:m-Relationen konfigurieren
Zur Instanziierung einer ezcPersistentManyToManyRelation werden drei Tabellennamen benötigt, da eine n:m-Relation in der Datenbank mittels einer Verknüpfungstabelle realisiert wird. Der erste Parameter zum Konstruktor ist die Tabelle des aktuellen Objekts – die Quelltabelle. Der zweite Parameter ist, wie bei 1:n-Relationen, die Zieltabelle und der dritte Parameter definiert die Verknüpfungstabelle.
Parallel dazu benötigen Sie für die Definition des $columnMap-Attributs Instanzen der Klasse ezcPersistentDoubleTableMap. Die Reihenfolge der Parameter für deren Konstruktor bildet die Verknüpfung ab, wie sie in der Datenbankabfrage benötigt wird: Zuerst wird die Spalte der Quelltabelle übergeben, welche mit einer Spalte der Verküpfungstabelle verbunden wird, die als zweiter Parameter zu übergeben ist. Die letzten beiden Werte definieren das Mapping zwischen Verknüpfungs- und Zieltabelle: Parameter Nummer drei gibt die Spalte in der Verknüpfungstabelle an, die auf den vierten Parameter, der Spalte in der Zieltabelle, bezogen wird. Wie bei 1:n-Relationen ist es auch hier möglich, mehrere Spalten-Verknüpfungen anzugeben.
Die Option $cascade wird von ezcPersistentManyToManyRelation nicht unterstützt. Es würde keinen Sinn machen, verbundene Objekte automatisch zu löschen, da diese noch Beziehungen zu anderen Objekten aufweisen können. Die betroffenen Datensätze der Verknüpfungstabelle werden aber automatisch entfernt, sodass Sie sich nicht darum kümmern müssen.
Es kann nicht nur interessant sein, zu einem Blog-Eintrag alle Kommentare zu finden, sondern auch den zu einem Kommentar gehörenden Blog-Eintrag zu erhalten. Aus diesem Grund wird auch in der zu gpBlogComment gehörenden Konfigurationsdatei eine entsprechende Relation definiert. Auf die Darstellung der Basiskonfiguration für diese Klasse verzichten wir aus Platzgründen an dieser Stelle. Sie unterscheidet sich nur durch Änderungen der entsprechenden Bezeichner von der bereits vorgestellten Konfiguration für das Eintragsmodell.
Wiederum taucht die folgende Relationsdefinition zwischen der Basiskonfiguration und der Rückgabe der Konfigurationsobjekte in der Datei persistent/gpblogcomment.php auf.
$def->relations['gpBlogEntry'] =ð new ezcPersistentManyToOneRelation( "comment", "entry" ); $def->relations['gpBlogEntry']->columnMap = array( new ezcPersistentSingleTableMap( "entry_id", "id" ), );
Listing 7.8 Umkehr-Relationen konfigurieren
Ein Kommentar gehört immer genau zu einem Blog-Eintrag, was eine 1:1-Relation induziert. Auch für solche Beziehungen existiert im PersistentObject-Paket eine Klasse mit dem Namen ezcPersistentOneToOneRelation. Allerdings wissen Sie, dass es sich in diesem konkreten Fall um die Umkehr-Richtung einer 1:n-Relation handelt. Es kann also mehrere Kommentare geben, die zu einem Blog-Eintrag gehören, weshalb statt der 1:1-Beziehung eine n:1-Beziehung mit Hilfe der Klasse ezcPersistentManyToOneRelation verwendet wird. Die Definition dieses Relationstyps erfolgt völlig analog zur Hin-Richtung.
Alle weiteren Relationsklassen besitzen ein Konfigurationsflag, welches die Relation als Umkehrung einer bereits existierenden Relation zu definieren erlaubt. Für die ezcPersistentManyToOneRelation gilt dies nicht, da eine solche Beziehung immer eine Rück-Richtung darstellt und für sich alleine keinen Sinn ergibt. Die Einstellung $reverse hat also immer den Wert true, welcher nicht geändert werden kann, und bildet somit immer die Rück-Richtung einer 1:n-Relation.
Die Konfiguration als Umkehrung einer bereits bestehenden Relation dient der Vorbeugung unbeabsichtigter Konflikte in der Datenbank. So kann zwar eine Relation zwischen zwei Objekten über die Hin-Richtung etabliert werden, jedoch nicht mit der Rück-Richtung. Auf das gezeigte Beispiel bezogen bedeutet dies, dass Sie zwar einem Eintrag einen Kommentar hinzufügen können, jedoch nicht einem Kommentar einen Eintrag. Allein diese zwei Formulierungen zeigen den Sinn hinter dem $reverse-Attribut sehr deutlich. Die explizite Konfiguration einer Relation als Umkehrung können Sie sich auf der Buch-CD in der Datei persistent/gpblogtag.php ansehen, in der die Umkehrrichtung zur bereits bestehenden Relation zwischen Einträgen und Tags definiert wird.
Damit haben Sie alle bisher verfügbaren Konfigurationsmöglichkeiten für persistente Objekte kennengelernt und können einen Schritt weitergehen, zur eigentlichen Verwendung der Komponente. Im Folgenden werden Sie sehen, wie die soeben konfigurierten Objekte praktisch in der Datenbank verwaltet werden und wie wenig Aufwand dazu noch nötig ist. Außerdem wird gezeigt, wie verbundene Objekte aus der Datenbank geladen, neue Verbindungen etabliert und Relationen aufgehoben werden können.




Ihre Meinung






