[1.14] [GORM] modellierungserweiterung

Dieser Bereich dient dazu, neue Features zu diskutieren und für die Entwicklung zu dokumentieren. // This area is dedicated to new features including proposals and documentation.
Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

[1.14] [GORM] modellierungserweiterung

Beitrag von Screeze » 08.01.2011, 00:34:54

Ich hatte gerade einen Blitzgedanken (über was man so alles nachdenkt wenn man eigentlich schon versucht einzuschlafen...) den ich mal diskutieren möchte. Der titel ist nicht besonders belungen - ich hab schlichtweg keine Ahnung wie man das nennen soll/nennt.

Der gorm ist zwar schon eine große hilfe, bringt aber einen entscheidenden nachteil mit sich (jedenfalls geht es mir so): man neigt dazu den oop gedanken zu verweichen. Jedenfalls ist mir aufgefallen,das ich deutlich mehr „objektmanager” schreibe, als objekte selber, weil ich als objekt ein GDO verwende. Die apis werden dadurch immer weniger intuitiv. Jetzt könnte ich für jedes Gdo zwar einen wrapper schreiben, aber müsste dann immer wieder wenn ich ein gdo geladen habe dieses erst dem wrapper übergeben, was deutlich mehr schreibaufwand erfordert. Ein objekt zu erstellen, das sein eigenes gdo läd und verwaltet ist wegen beziehungsabfragen nicht wirklich möglich, irgendwie bekommt man irgendwo doch wieder ein gdo übergeben.
Jetzt zu meiner Idee. Vorstellen könnte ich mir das als optionales feature des gorm, oder als erweiterung des gorm zu einem xyzGORM, was die wartung aber deutlich erschwert.
Was ich mir vorstelle ist eine möglichkeit, bei bestimmten objekten (in der objects.ini, einer eigenen .ini oder auch einfach per parameter gesteuert) anstatt des gdo, ein vom gdo abgeleietes objekt zurückzubekommen, das die für dieses objekt nötigen methoden zur verfügung stellt.
Villeicht könnte man zusätzlich einen internen caching mechanismus einbauen, sodass jedes objekt einer art mit einer best. Id nur einmal existiert möglichst. Richtig umgesetzt könnte das a. Performancevorteile bringen, und b. Das handling erleichtern.

Ich würde jetzt gerne mal diskutieren inwieweit was davon umsetzbar wäre, oder ob ich schon (wunsch-)träume ;)

Ich würd ja pseuydocode liefern, aber ich tipp vom handy, is weng doof, sorry auch wegen grober tippfehler deswegen ;)

Megger
Beiträge: 1233
Registriert: 04.11.2008, 10:57:37

Re: [GORM] modellierungserweiterung

Beitrag von Megger » 08.01.2011, 08:01:53

Klingt recht interessant der Gedanke

Zur Umsetzung habe ich allerdings auch keine Idee
Würde das ganze aber wahrscheinlich über die objects.ini oder eine eigene optionale .ini machen auf die der GORM reagiert wenn sie vorhanden ist.
Schön wäre es natürlich auch, dass wenn man das ganze vom User Objekt macht, direkt Methoden wie getUserID usw. vorhanden sind (weiß nicht inwieweit das möglich ist)
Tutorial: Browsergame mit dem APF (Die ersten Parts handeln von Installation und Inbetriebnahme des APFs, deswegen sicherlich auch für alle Nicht-Browsergame-Programmierer interessant)

APF-Version
  • Entwicklung: 2.0
  • Produktiv: 1.15

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: [GORM] modellierungserweiterung

Beitrag von Screeze » 08.01.2011, 11:53:00

Nunja die Umsetzung scheint relativ einfach:

In der objects.ini oder einer anderen config, wird zu dem objekt ein Namespace und ein Name übergeben:

Code: Alles auswählen

[User]
Props = "values"
ObjectNamespace = "bla::bla"
ObjectName = "User" ; oder auch "MyUser" whatever
der GORM prüft dort wo er objekte erzeugt, ob das vorhanden ist, wenn ja läd er das objekt, und behandelt es anschließend wie ein GDO, ansonsten läd er ganz normal sein GDO.
Schön wäre es natürlich auch, dass wenn man das ganze vom User Objekt macht, direkt Methoden wie getUserID usw. vorhanden sind (weiß nicht inwieweit das möglich ist)
Bei der ID halte ich es war für nicht so wichtig, es gibt ja getObjectID() bereits, das reicht eigentlich, aber theoretisch könnte man hier mit der magischen __call Methode arbeiten, und eine get/set möglichkeit für alle attribute bereitstellen:

Code: Alles auswählen

class GenericDomainObject {
 //...

   ___call($name, $arguments){
      // zerlegen von $name nach 3 Buchstaben, prüfen ob die ersten 3 buchstaben "get" lauten
      if (...){
            if(isset($this->Properties[$restVonName])){
                  return $this->Properties[$restVonName];
            }
            // hier noch ne exception werfen wenn property nicht vorhanden
     }
// prüfen ob die ersten 3 Buchstaben "set" lauten
    elseif (...){
          // setzen eines properties, wenn vorhanden mit dem als argument übergebenen wert
   }
   else {
          // exception werfen, unbekannte methode wurde aufgerufen.
   }
}
(quickndirty code, keine ahnung wie das Properties array intern lautet, grad keine IDE zur hand)
Dann wäre sowas möglich:

Code: Alles auswählen

$prop = $GDO->getMyWonderfulProp(); // entspricht $GDO->getProperty('MyWonderfulProp');
$GDO->setMyWonderFulProp($prop); // -> $GDO->setProperty('MyWonderfulProp', $prop);

Benutzeravatar
dr.e.
Administrator
Beiträge: 4538
Registriert: 04.11.2007, 16:13:53

Re: [GORM] modellierungserweiterung

Beitrag von dr.e. » 08.01.2011, 13:17:02

Hallo Ralf,

deine Idee geht in Richtung Generierung von Model- bzw. domain model-Klassen. Der Gedanke ist weder Wunschtraum noch abwegig. Zu realisieren wäre das meiner Ansicht nach recht einfach, in dem du eine zusätzliche Konfigurations-Datei anbietest die das Mapping zwischen GDO und DO (domain object) definiert. Eine "DomainObjectFactory" könnte diese dann nutzen um dem GORM bei der Transformation zu assistieren. Wird der GORM per DI mit dieser erzeugt gibt er DO's zurück ansonsten GDO's. Da das Objekt-Mapping im GORM recht einfach und zentral gehalten ist, macht das an sich wenig Aufwand. Einziger Nachteil: der GORM muss in diesem Fall mit dem DIServiceManager erzeugt werden. Für mich persönlich ist das kein Problem, für Einsteiger vielleicht doch eine Hürde mehr. Aber dieser kann sich ja "mit GDO's zufrieden geben".

Zu diskutieren bleibt IMHO der Rückweg. Das GDO bzw. DO müsste dann mindestens ein gemeinsames Interface implementieren und die "DomainObjectFactory" bräuchte ein Pendant (z.B. "GenericDomainObjectFactory"), die den Rückweg (DO -> GDO) sicherstellt.

Anschließend könnte man einen Klassengenerator schreiben, der die DO's generiert und in den DO's kannst du dann entsprechende Logik für den DO implementieren. So einen ähnlichen Ansatz pflegt auch Coremedia. Dort hast du eine XML-Datei, die die Content-Beans definiert und mit einem Generator kannst du dir Interfaces und Stub-Klassen erzeugen. In den Stub-Klassen kannst du dann eigene Implementierungen vorsehen. Dort wird für das Mapping allerdings ein etwas anderer Ansatz gefahren: das Mapping übernimmt eine Base-Klasse des DO. Das geht in etwa so:

Code: Alles auswählen

class GenericDomainObject {
}

class UserBase {

   public function __construct(GenericDomainObject $gdo){
      $this->gdo = $gdo;
   }

   public function getUserID(){
      $this->gdo->getProperty('UserID');
   }

   public function setUserID($id){
      $this->gdo->setProperty('UserID', $id);
   }

   ...

   public function getGDO{
      return this->gdo;
   }
   
}

class UserImpl extends UserBase implements GORMDomainObject {

   // diese Klasse wird für die Impleemntierung der eigenen Funktionalität genutzt

}
Der GORM gibt dann Objekte vom Interface-Typ GORMDomainObject zurück, die jeweils eigene Implementierungen haben können. Beim Speichern kannst du dann im GORM vom Domänen-Objekt das GDO beziehen und entsprechend speichern. Die Klasse UserImpl könntest du dann dazu nutzen explizite, deklarative Methoden für das Laden und Manipulieren von Beziehungen zu implementieren. Im UserImpl hast du dann eine Instanz des GDO und damit des GORM und kannst alles anstellen.

Was denkt ihr?
Viele Grüße,
Christian

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: [GORM] modellierungserweiterung

Beitrag von Screeze » 08.01.2011, 13:22:53

Meine Idee war eher folgendermaßen gedacht:

Code: Alles auswählen

class User extends GenericDomainObject{
}
Jedes DO MUSS zwangsläufig vom GDO erben, damit sparst du dir dann auch
Zu diskutieren bleibt IMHO der Rückweg. Das GDO bzw. DO müsste dann mindestens ein gemeinsames Interface implementieren und die "DomainObjectFactory" bräuchte ein Pendant (z.B. "GenericDomainObjectFactory"), die den Rückweg (DO -> GDO) sicherstellt.
Ist ja egal ob der User auch die GDO funktionen liefert, im gegenteil, ich finds praktischer, dann kann ich ihn wie ein GDO verwenden, hab aber zusätzlich noch die benötigte Logik.

Dazu noch die __call() methode von oben, dann brauchts eigentlich keinen generator, wenn eine bestimmte setter/getter methode zusätzliche logik braucht, kann man die ja trotzdem manuell schreiben schnell, der rest läuft über die __call methode.
Einziger Nachteil: der GORM muss in diesem Fall mit dem DIServiceManager erzeugt werden. Für mich persönlich ist das kein Problem, für Einsteiger vielleicht doch eine Hürde mehr. Aber dieser kann sich ja "mit GDO's zufrieden geben".
Das wiederum verstehe ich nicht - wieso?

Benutzeravatar
dr.e.
Administrator
Beiträge: 4538
Registriert: 04.11.2007, 16:13:53

Re: [GORM] modellierungserweiterung

Beitrag von dr.e. » 08.01.2011, 13:31:15

Hallo Ralf,

deine Idee hatte ich auch so verstanden, nur sehe ich da einen großen Nachteil: generics + IDE. Keine IDE der Welt versteht, dass ein getFoo() auf einem Objekt aufgerufen werden kann und zeigt dir das in der Code-Vervollständigung an, wenn du __call() implementierst. Das würdest du mit einer generierten UserBase-Klasse nicht haben, weil diese die getter/setter explizit formuliert. Sicher ist es einfacher, aber meiner Ansicht hast du dann wieder das Problem in die Config schauen zu müssen, was das aktuelle Objekt denn kann.
Jedes DO MUSS zwangsläufig vom GDO erben, damit sparst du dir dann auch
Nutzen wir das decorator pattern (wie unten aufgezeigt) entfällt das Mappen auch, weil der Master immer noch ein GDO ist.
Das wiederum verstehe ich nicht - wieso?
Wenn du für die Erzeugung der Objekte das Factory-Pattern nutzt und nicht das Decorator Pattern, hast du die Notwendigkeit dem GORM die Abhängigkeit zur Factory zu injizieren und der Factory die Config. Das machst du am besten mit DI und wahrscheinlich auch nur mit DI, denn sonst wird das im Code einfach grässlich.
Viele Grüße,
Christian

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: [GORM] modellierungserweiterung

Beitrag von Screeze » 08.01.2011, 13:55:29

deine Idee hatte ich auch so verstanden, nur sehe ich da einen großen Nachteil: generics + IDE. Keine IDE der Welt versteht, dass ein getFoo() auf einem Objekt aufgerufen werden kann und zeigt dir das in der Code-Vervollständigung an, wenn du __call() implementierst. Das würdest du mit einer generierten UserBase-Klasse nicht haben, weil diese die getter/setter explizit formuliert. Sicher ist es einfacher, aber meiner Ansicht hast du dann wieder das Problem in die Config schauen zu müssen, was das aktuelle Objekt denn kann.
Ok ist ein argument, allerdings das mit in die config schauen würd ich garnicht machen, ich würd einfach nachschaun ob ein property mit dem Name geladen wurde oder nicht. Aber ok, bin überzeugt was das angeht.
Nutzen wir das decorator pattern (wie unten aufgezeigt) entfällt das Mappen auch, weil der Master immer noch ein GDO ist.
Aber ich kann das Objekt nicht einfach als GDO verwenden, sondern muss auf das interne GDO zurückgreifen, find ich umständlich.
Wenn du für die Erzeugung der Objekte das Factory-Pattern nutzt und nicht das Decorator Pattern, hast du die Notwendigkeit dem GORM die Abhängigkeit zur Factory zu injizieren und der Factory die Config. Das machst du am besten mit DI und wahrscheinlich auch nur mit DI, denn sonst wird das im Code einfach grässlich.
Dann nehmen wir kein Factory Pattern? :D

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: [GORM] modellierungserweiterung

Beitrag von Screeze » 08.01.2011, 16:21:48

Als config beispiel würde ich vorschlagen:

In der DEFAULT_domainobjects.ini (oder _objectmodels.ini) o.ä. werden für die Objekte, bei denen man sein eigenes Model möchte, eine sektion mit namespace und name angelegt:

Code: Alles auswählen

[User]
Namespace = "my::namespace::to::user::model"
Name = "User"
Falls nicht vorhanden wird natürlich das normale GDO genommen.

Benutzeravatar
dr.e.
Administrator
Beiträge: 4538
Registriert: 04.11.2007, 16:13:53

Re: [GORM] modellierungserweiterung

Beitrag von dr.e. » 08.01.2011, 19:01:23

Hallo Ralf,

ich denke wir nehmen keine Factory. :)
Aber ich kann das Objekt nicht einfach als GDO verwenden, sondern muss auf das interne GDO zurückgreifen, find ich umständlich.
Hier muss man meiner Ansicht nach "umständlich" gegen "semantisch" abwägen. Nutzt du "nur" eine Erweiterung eines GDO kannst du zwar neben getFoo() auch getProperty('Foo') aufrufen, aber nutzt effektiv auch nur das GDO. Nutzt du einen Wrapper, hast du IMHO das gleiche Problem, dass es mehr oder weniger "auch nur" ein GDO ist, aber die Methoden werden direkt mit generiert und du musst diese nicht extra implementieren. Weiterhin hast du eine Trennung zwischen reinem DTO (dem GDO und der Hülle des Wrappers) und deiner eigentlichen Implementierung.

Wir können aber auch gerne mit einem Wrapper beginnen dem per Default einfach die Methoden mit eingebaut werden. Dann verlieren wir aber die Trennung zwischen der Benutzer-spezifischen Implementierung und eigentlichem DTO.

Auf Arbeit haben wir z.B. in einer Klasse PromotionBase einfach die getter und setter generiert und in der PromotionImpl diese überschrieben um eine fachlich geforderte Fallback-Logik zu implementieren. Das würde mit deiner Variante nicht ganz so elegent lösbar sein, weil du die getter des eigentlichen DO's änderst. Damit gestaltet sich die Generierung der Klassen wieder umständlicher, weil du darauf achten musst, ob der Inhalt einer generierten Methode nun verändert wurde oder nicht. Wenn er verändert wurde, kannst du quasi nichts machen und musst eine Warning schmeissen. Haben wir eine Basis-Klasse für die Wrapper-Implementierung, kann diese immer komplett generiert werden. Für die *Impl-Klasse kannst du dann einfach einen Stub generieren, wenn diese noch nicht existiert oder einfach nichts tun, wenn sie schon da ist.

Was die Config angeht, bin ich einverstanden, mehr braucht es an sich nicht, denn die Abhängigkeiten der Klasse User sind dann einfach in der UserImpl-Klasse vom Entwickler selbst abzubilden (z.B. Laden von speziellen Beziehungs-Objekten, ...). Sofern der GORM über den DIServiceManager erzeugt wird, kann man sich den GORM auch dort holen.

Alternativ - das geht aber auf die Performance - könnte man die Config so schreiben, dass sie vom DIServiceManager verstanden wird. Das hätte den Vorteil, dass du einem DO jeden beliebigen Service zusätzlich injizieren könntest. Halte ich aber fast für übertrieben. Oder hast/hättest du da einen Anwendungsfall?

@Tobi: was denkst du?
Viele Grüße,
Christian

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: [GORM] modellierungserweiterung

Beitrag von Screeze » 08.01.2011, 19:21:31

Hier muss man meiner Ansicht nach "umständlich" gegen "semantisch" abwägen. Nutzt du "nur" eine Erweiterung eines GDO kannst du zwar neben getFoo() auch getProperty('Foo') aufrufen, aber nutzt effektiv auch nur das GDO. Nutzt du einen Wrapper, hast du IMHO das gleiche Problem, dass es mehr oder weniger "auch nur" ein GDO ist, aber die Methoden werden direkt mit generiert und du musst diese nicht extra implementieren. Weiterhin hast du eine Trennung zwischen reinem DTO (dem GDO und der Hülle des Wrappers) und deiner eigentlichen Implementierung.

Wir können aber auch gerne mit einem Wrapper beginnen dem per Default einfach die Methoden mit eingebaut werden. Dann verlieren wir aber die Trennung zwischen der Benutzer-spezifischen Implementierung und eigentlichem DTO.

Auf Arbeit haben wir z.B. in einer Klasse PromotionBase einfach die getter und setter generiert und in der PromotionImpl diese überschrieben um eine fachlich geforderte Fallback-Logik zu implementieren. Das würde mit deiner Variante nicht ganz so elegent lösbar sein, weil du die getter des eigentlichen DO's änderst. Damit gestaltet sich die Generierung der Klassen wieder umständlicher, weil du darauf achten musst, ob der Inhalt einer generierten Methode nun verändert wurde oder nicht. Wenn er verändert wurde, kannst du quasi nichts machen und musst eine Warning schmeissen. Haben wir eine Basis-Klasse für die Wrapper-Implementierung, kann diese immer komplett generiert werden. Für die *Impl-Klasse kannst du dann einfach einen Stub generieren, wenn diese noch nicht existiert oder einfach nichts tun, wenn sie schon da ist.
Hmm Mist, das ist ein gutes Argument, über das ich nicht nachgedacht hatte...

Alternativ - das geht aber auf die Performance - könnte man die Config so schreiben, dass sie vom DIServiceManager verstanden wird. Das hätte den Vorteil, dass du einem DO jeden beliebigen Service zusätzlich injizieren könntest. Halte ich aber fast für übertrieben. Oder hast/hättest du da einen Anwendungsfall?
Nicht wirklich, halte ich auch für Übertrieben.


Wie würdest du dann die Rückspeicherung realisieren?
ein $GORM->saveObject($User); geht ja dann nichtmehr, wenn $User nicht das GDO ist. Müsste ich dann immer $GORM->saveObject($User->getGDO()); verwenden?
Oder sollte der User sein GORM kennen und ein $User->save(); ausführen können?

Benutzeravatar
dr.e.
Administrator
Beiträge: 4538
Registriert: 04.11.2007, 16:13:53

Re: [GORM] modellierungserweiterung

Beitrag von dr.e. » 08.01.2011, 20:10:15

Hallo Ralf,

letzteres würde ich einfach so realisieren, dass ein GDO per Interface eine Methode getGDO() hat und dann einfach $this zurückliefert. Die Überschreibung/Implemenierung eines DO's würde dann sein internes GDO zurückgeben. Damit kann der GORM immer intern nach getGDO() fragen.

Weiter sollte es ein Interface geben, das beide implementieren, dann bleibt das GORM-Interface wie auch jetzt konsistent.
Viele Grüße,
Christian

Benutzeravatar
dr.e.
Administrator
Beiträge: 4538
Registriert: 04.11.2007, 16:13:53

Re: [GORM] modellierungserweiterung

Beitrag von dr.e. » 08.01.2011, 20:11:37

Nachtrag:
Oder sollte der User sein GORM kennen und ein $User->save(); ausführen können?
Das kennt er heute schon und damit könnte man ein save() bereits realisieren. Einschränkungen gibt es nur dann, wenn das Objekt nicht direkt vom GORM geladen wurde. Das könnte man aber dadurch lösen, dass man den GORM über DI erzeugt...
Viele Grüße,
Christian

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: [GORM] modellierungserweiterung

Beitrag von Screeze » 08.01.2011, 20:27:25

Okay klingt vernünftig.

Ich fasse nochmal zusammen wie ich das jetzt verstanden habe:

- Interface für (G)DO's (getGDO(), ... (was braucht es hier noch?))
- DO's halten intern ein GDO als Datenobjekt, halten selbst aber die Logik um damit zu arbeiten
- GORM erzeugt aufgrund einer dritten, optionalen .ini Datei DO's statt GDOs, wenn definiert, und kann mit diesen auch wieder umgehen
- DO bekommt im constructor ein GDO, oder erzeugt ein leeres wenn keins übergeben wurde

- Ein Klassengenerator soll anhand der objects.ini und der neuen .ini entsprechende Grundgerüste generieren und zur verfügung stellen. (getter, setter, interface)

Änderungen am GORM die nötig wären:
- GenericORMapper::__mapResult2DomainObject(..) -> unterscheidung ob GDO oder DO erzeugt werden muss
- Alle Methoden die ein GDO verwenden müssen erst auf $GDO->getGDO() zugreifen, und dann damit arbeiten
- Bei Parametern muss die typangabe vom GDO auf das neue interface geändert werden

Hab ich was vergessen?

Benutzeravatar
dr.e.
Administrator
Beiträge: 4538
Registriert: 04.11.2007, 16:13:53

Re: [GORM] modellierungserweiterung

Beitrag von dr.e. » 08.01.2011, 21:16:25

Hallo Ralf,

die Liste der Anpassungen ist fast komplett. Es fehlen noch die Mechanismen die neue Config-Datei einzulesen und neue, zusätzliche Configs hinzuzufügen. Hier gibt es zwei Wege: Standard und DI. Für DI muss gemäß http://wiki.adventure-php-framework.org ... iceManager eine eigene Methode und ein Config-Objekt geschrieben werden. Ist nicht viel, aber vergessen dürfen wir das nicht.

Wenn du schon loslegen magst, würde ich vorschlagen, dass ich einen 1.14-Branch anlege oder noch besser du deine Änderungen einfach lokal durchführst und wir diese dann File-basiert austauschen und diskutieren (oder natürlich über Teamviewer).
Viele Grüße,
Christian

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: [GORM] modellierungserweiterung

Beitrag von Screeze » 08.01.2011, 21:26:29

Ich kann mich da gerne morgen dran setzen. branch brauchst du noch nicht aufmachen denke ich, lass das erstmal lokal probieren.

Gibts in der sandbox irgendwas das den GORM verwendet?

Gesperrt

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast