Erzeugen des GORM mit dem DIServiceManager
Aus APF Wiki [de]
Inhaltsverzeichnis |
Einleitung
Der GORM kann ab dem Release 1.12 (final) und dem SVN-Code-Stand des php5/1.12-Branch ab Commit #986 auch mit dem DIServiceManager erzeugt werden. Dies hat den Vorteil, dass die explizite Initialisierung nicht mehr im Quellcode stattfinden muss, sondern in eine Konfiguration ausgelagert werden kann. Weitere Vorteile ergeben sich für die Testbarkeit einer Komponente, die auf dem GORM basiert und die Entkoppelung der Komponenten wichtig.
Die folgenden Kapitel zeigen, wie die Erzeugung und Initialisierung mit dem DIServiceManager des Generische O/R-Mapper technisch funktioniert.
Erzeugen des GORM
Für die Erzeugung des O/R-Mapper wird bisher eine Factory angeboten, die den Mapper mit den relevanten Informationen des Frameworks und der gewünschten Konfiguration versorgt. Das Konzept der dependency injection besagt jedoch, dass nicht mehr eine aufrufende Komponente für die Initialisierung der Abhängigen zuständig ist, sondern ein Mediator, bzw. implizit der Service selbst. Hierzu wird in der JAVA-Welt oft ein Dependency-Injection-Container wie in Spring enthalten genutzt. Das APF besitzt für diese Aufgabe den DIServiceManager, der Abhängigkeiten zwischen Services auflösen und korrekt konfigurierte Services zur Verfügung stellen kann.
Der DIServiceManager erwartet hierzu eine Konfiguration, die den Service auf einer Meta-Ebene beschreibt und namentlich eindeutig kennzeichnet. Ein Service zeichnet sich im allgemeinen durch ein Mapping auf eine konkrete Implementierung und statische sowie dynamische Konfigurationen aus. Dynamische Konfigurationen sind solche, die einen abhängigen Service in einen anderen Service injizieren. So kann beispielsweise ein Datenbank-Adapter in einen Business-Service injiziert werden, damit dieser auf die relevante Datenbank-Resource Zugriff hat.
Im Fall des GORM ist es notwendig eine Konfiguration zu injizieren, die die Initialisierung des Mappers übernimmt. Diese umfasst
- Konfigurations-Namespace
- Suffix der Konfigurations-Datei
- Name der zu nutzenden Datenbank-Verbindung
- Anweisungen für den Datenbank-Treiber
Als Container-Format erwartet der GORM eine Instanz der Klasse GenericORMapperDIConfiguration. Dies lässt sich in der Service-Notation wie folgt abbilden:
[GORM-CONFIG] servicetype = "NORMAL" namespace = "modules::genericormapper::data" class = "GenericORMapperDIConfiguration" conf.namespace.method = "setConfigNamespace" conf.namespace.value = "..." conf.affix.method = "setConfigAffix" conf.affix.value = "..." conf.conn.method = "setConnectionName" conf.conn.value = "..." conf.debug.method = "setDebugMode" conf.debug.value = "true|false" ;conf.ext.method = "setConfigFileExtension" ;conf.ext.value = "orm"
Die ab Version 1.14 vorhandene optionale Sektion conf.ext dient zur Definition der Datei-Endung für GORM-Konfigurationen (Objekte, Beziehungen, Domänen-Objekte). Diese kann im Rahmen der APF-Konfigurations-Schemen gleichzeitig für das Mapping auf den gewünschten ConfigurationProvider genutzt werden.
Mit dieser Definition wurde bereits ein vollständiger Service definiert, der in diesem Fall zur Initialisierung des GORM dient. Über
[GORM] servicetype = "SINGLETON" namespace = "modules::genericormapper::data" class = "GenericORRelationMapper"
können wir nun den Service des GORM selbst definieren und mit
[GORM] servicetype = "SINGLETON" namespace = "modules::genericormapper::data" class = "GenericORRelationMapper" init.configure.method = "initDI" init.configure.namespace = "..." init.configure.name = "GORM-CONFIG"
korrekt initialisieren. Bei der Beispiel-Konfiguration ist zu beachten, dass die Namen der Sektionen (=Service-Name) frei wählbar sind. Die Direktive init.configure.namespace definiert dabei den Namespace der Service-Konfiguration, die den Service GORM-CONFIG enthält. Sind beide in de gleichen Datei enthalten, ist der Namespace zum Aufruf des GORM-Service mit diesem gleichzusetzen.
Die hier beschriebene Art der Initialisierung ist nur in Version 1.14 möglich. Ab Version 1.15 wurde der O/R-Mapper vollständig für die Initialisierung über den DI-Container des APF fitt gemacht. Bitte hierzu das nächste Kapitel beachten.
Initialisierung GORM per DI ab Version 1.15
Ab Version 1.15 kann der GORM vollständig per DI-Container (siehe DIServiceManager) initialisiert werden. Hierzu ist die folgende Konfiguration notwendig:
[GORM] servicetype = "SESSIONSINGLETON" namespace = "modules::genericormapper::data" class = "GenericORRelationMapper" setupmethod = "setup" conf.namespace.method = "setConfigNamespace" conf.namespace.value = "modules::usermanagement::data" conf.affix.method = "setConfigNameAffix" conf.affix.value = "umgt" conf.db.method = "setConnectionName" conf.db.value = "Umgt" conf.debug.method = "setLogStatements" conf.debug.value = "false"
Neu hinzugekommen ist die Definition einer "setupmethod". Diese Methode wird einmalig pro Erzeugung des Objekts ausgeführt um das Objekt zu initialisieren.
Weitere Services
Sofern der GORM als Basis-Service in einer weiteren Komponente (hier: MyAppManager) dient, kann er sehr einfach per
[MyBusinessService] servicetype = "SINGLETON" namespace = "..." class = "MyAppManager" init.gorm.method = "setMapper" init.gorm.namespace = "..." init.gorm.name = "GORM"
in diesem Service injiziert werden.
Zusätzliche Mapping-Konfigurationen
Ein weiteres Feature des GORM ist die Möglichkeit, on-the-fly weitere Mapping-Konfigurationen hinzuzufügen um ein Basis-Set an Objekt-Definitionen für bestimmte Anwendungsfälle einfach erweitern zu können.
Für diesen Anwendungsfall steht der selbe Mechanismus wie oben beschrieben zur Verfügung - mit dem Unterschied, dass die dynamische Mapping-Konfiguration von der Klasse GenericORMapperDIMappingConfiguration repräsentiert wird.
Um einen GORM-Service mit einer zusätzlichen Mapping-Konfiguration auszustatten ist folgende Service-Konfiguration notwendig:
[GORM] servicetype = "SINGLETON" namespace = "modules::genericormapper::data" class = "GenericORRelationMapper" init.configure.method = "initDI" init.configure.namespace = "..." init.configure.name = "GORM-CONFIG" init.map.method = "addDIMappingConfiguration" init.map.namespace = "..." init.map.name = "GORM-CONFIG-MAPPING" [GORM-CONFIG] servicetype = "NORMAL" namespace = "modules::genericormapper::data" class = "GenericORMapperDIConfiguration" conf.namespace.method = "setConfigNamespace" conf.namespace.value = "..." conf.affix.method = "setConfigAffix" conf.affix.value = "..." conf.conn.method = "setConnectionName" conf.conn.value = "..." conf.debug.method = "setDebugMode" conf.debug.value = "true|false" [GORM-CONFIG-MAPPING] servicetype = "NORMAL" namespace = "modules::genericormapper::data" class = "GenericORMapperDIMappingConfiguration" conf.namespace.method = "setConfigNamespace" conf.namespace.value = "..." conf.affix.method = "setConfigAffix" conf.affix.value = "..."
Zusätzliche Beziehungs-Konfigurationen
Ein weiteres Feature des GORM ist die Möglichkeit, on-the-fly weitere Beziehungs-Konfigurationen hinzuzufügen um ein Basis-Set an Objekt-Definitionen für bestimmte Anwendungsfälle einfach erweitern zu können.
Dies funktioniert analog zur dynamischen Mapping-Konfiguration über die Klasse GenericORMapperDIRelationConfiguration:
[GORM] servicetype = "SINGLETON" namespace = "modules::genericormapper::data" class = "GenericORRelationMapper" init.configure.method = "initDI" init.configure.namespace = "..." init.configure.name = "GORM-CONFIG" init.rel.method = "addDIRelationConfiguration" init.rel.namespace = "..." init.rel.name = "GORM-CONFIG-RELATION" [GORM-CONFIG] servicetype = "NORMAL" namespace = "modules::genericormapper::data" class = "GenericORMapperDIConfiguration" conf.namespace.method = "setConfigNamespace" conf.namespace.value = "..." conf.affix.method = "setConfigAffix" conf.affix.value = "..." conf.conn.method = "setConnectionName" conf.conn.value = "..." conf.debug.method = "setDebugMode" conf.debug.value = "true|false" [GORM-CONFIG-RELATION] servicetype = "NORMAL" namespace = "modules::genericormapper::data" class = "GenericORMapperDIRelationConfiguration" conf.namespace.method = "setConfigNamespace" conf.namespace.value = "..." conf.affix.method = "setConfigAffix" conf.affix.value = "..."
Beispiel
Das folgende Beispiel zeigt die Konfiguration eines Business-Services, der eine GORM-Instanz mit erweiterterter Konfiguration nutzt:
[Manager] servicetype = "SINGLETON" namespace = "..." class = "MyAppManager" init.gorm.method = "setMapper" init.gorm.namespace = "..." init.gorm.name = "GORM" [GORM] servicetype = "SINGLETON" namespace = "modules::genericormapper::data" class = "GenericORRelationMapper" init.configure.method = "initDI" init.configure.namespace = "..." init.configure.name = "GORM-CONFIG" init.rel.method = "addDIRelationConfiguration" init.rel.namespace = "..." init.rel.name = "GORM-CONFIG-RELATION" init.map.method = "addDIMappingConfiguration" init.map.namespace = "..." init.map.name = "GORM-CONFIG-MAPPING" [GORM-CONFIG] servicetype = "NORMAL" namespace = "modules::genericormapper::data" class = "GenericORMapperDIConfiguration" conf.namespace.method = "setConfigNamespace" conf.namespace.value = "..." conf.affix.method = "setConfigAffix" conf.affix.value = "..." conf.conn.method = "setConnectionName" conf.conn.value = "..." conf.debug.method = "setDebugMode" conf.debug.value = "true|false" [GORM-CONFIG-MAPPING] servicetype = "NORMAL" namespace = "modules::genericormapper::data" class = "GenericORMapperDIMappingConfiguration" conf.namespace.method = "setConfigNamespace" conf.namespace.value = "..." conf.affix.method = "setConfigAffix" conf.affix.value = "..." [GORM-CONFIG-RELATION] servicetype = "NORMAL" namespace = "modules::genericormapper::data" class = "GenericORMapperDIRelationConfiguration" conf.namespace.method = "setConfigNamespace" conf.namespace.value = "..." conf.affix.method = "setConfigAffix" conf.affix.value = "..."
Der Aufruf des Business-Services erfolgt innerhalb eines Controllers - bzw. jeder Instanz der Klasse APFObject wie folgt:
$manager = &$this->getDIServiceObject('...','Manager');
Der Namespace ist gemäß der Ablage der Konfigurations-Datei zu wählen. Details können dem Kapitel Konfiguration der APF-Dokumentation entnommen werden.
Konfiguration der Datei-Endung
Ab dem Release 1.14 gibt es drei Möglichkeiten, die Datei-Endung für GORM-Konfigurationen (*_objects.ini, *_relations.ini, *_serviceobjects.ini) zu definieren:
Factory
Die GenericORRelationMapperFactory implementiert ab 1.14 die ServiceManager-Methode init(). Über diese kann die Datei-Endung vor dem Erzeugen des GORM injiziert werden:
$orm = $this->getAndInitServiceObject( 'modules::genericormapper::data', 'GenericORMapperFactory', 'orm') ->getGenericORMapper( 'modules::usermanagement', 'umgt', 'my-db' );
Die in die Factory injizierte Endung wird bei der Erzeugung auch in den O/R-Mapper injiziert.
Indirekte DI-Konfiguration
Wie auch in der Version 1.13 möglich, kann der GORM über den DIServiceManager (siehe oben) erzeugt werden. Hierzu muss bei der Definition der Konfiguration (GenericORMapperDIConfiguration) darauf geachtet werden, dass die Service-Sektion die Datei-Endung via
[GORM-CONFIG] ... conf.ext.method = "setConfigFileExtension" conf.ext.value = "orm"
initialisiert wird. Die Information der Datei-Endung wird von dort dann in den Mapper eingeimpft.
Direkte DI-Konfiguration
Ab dem Release 1.14 kann der GORM auch direkt über den DIServiceManager erzeugt werden. Hierzu wurden entsprechende Methoden definiert, die die Konfiguration injizieren und den Mapper initialisieren können.
Zur Definition eines GORM-Service ist folgende Konfiguration notwendig:
[GORM] servicetype = "SINGLETON" namespace = "modules::genericormapper::data" class = "GenericORRelationMapper" conf.ext.method = "setConfigFileExtension" conf.ext.value = "orm" conf.namespace.method = "setConfigNamespace" conf.namespace.value = "..." conf.affix.method = "setConfigNameAffix" conf.affix.value = "..." conf.conn.method = "setConnectionName" conf.conn.value = "..." conf.debug.method = "setLogStatements" conf.debug.value = "true|false"
Dabei ist darauf zu achten, dass die Reihenfolge der Initialisierung eingehalten ist. Wird die Methode setConfigFileExtension() beispielsweise nicht vor setConfigNameAffix() aufgerufen, wird die Standard-Endung (.ini) bei der Initialisierung genutzt.