OPUS4 / framework

OPUS 4 Database Implementation
Other
2 stars 7 forks source link

Opus\Language mit Doctrine-ORM implementieren #127

Open j3nsch opened 2 years ago

j3nsch commented 2 years ago

Language ist eines der einfachsten Modelle im OPUS 4 Datenmodell. Die Klasse wird nur an wenigen Stellen verwendet und die Datenbanktabelle hat keine Foreign-Key-Beziehungen. Language ist also ein sehr guter Kandidat, um erste Erfahrungen mit Doctrine-ORM zu sammeln und zu testen, ob es mit Zend-DB und Doctrine-DBAL koexistieren kann. Durch die parallele Nutzung dieser Technologien könnte die Datenbankanbindung schrittweise umgestellt werden.

Ein weiteres wichtiges Ziel dieser Umsetzung ist herauszufinden, wo die Schwierigkeiten beim Umbau der Datenmodellklassen liegen.

Hinweise zur Umsetzung (erste Gedanken)

Im Idealfall sollte die neue Language-Klasse sich von außen genauso verhalten wie die aktuelle Implementation, so dass sie von der OPUS 4 Application ohne Anpassungen dort genutzt werden kann. Dafür müsste die Klasse dann aber genau den gleichen Namen haben, die existierende Klasse also ersetzt werden. Langfristig macht es vermutlich Sinn den Namen in etwas wie Opus\Model\Language umzuwandeln. Das würde aber im Augenblick unnötige zusätzliche Komplikationen verursachen und kann später bereinigt werden.

Die neue Klasse kann nicht mehr von Opus\Model\AbstractDb abgeleitet werden, dass die aktuelle Datenbankanbindung mit Zend-Db implementiert. Auch eine Ableitung von Opus\Model\AbstractModel ist nicht sinnvoll, da diese Klasse die aktuellen Strukturen mit Field-Objekten implementiert, die die Grundlage für die "ORM"-Funktionalität in der existierenden Datenbankanbindung ist. Die neue Klasse sollte wesentlich einfacher aufgebaut sein, so wie es mit Doctrine-ORM üblich ist. Um die gleich API wie andere, alte Modelklassen zu bieten könnte evtl. eine neue abstrakte Modelklasse eingeführt werden, von der auch AbstractModel abgeleitet wird. Alternativ könnte es auch ein Interface sein oder wir verlassen uns für den Augenblick darauf, das die Typenkontrolle in PHP sehr flexibel ist.

Die Funktionen, die Language erbt, müssen einzeln betrachtet werden. Es wäre im Augenblick zum Beispiel nützlich die Funktion storezu erhalten, damit sie vom CRUD-Controller in der Application genutzt werden kann. Die Store-Funktionen sollte aber die Verarbeitung an eine andere unabhängige Klasse delegieren und nur als Fassade dienen. Die neuen Modelklassen sollten unabhängig von der eigentlich Datenbankanbindung sein, so dass diese später auch durch andere Technologien ausgetauscht werden kann.

Die Instanzierung von Language sollte nicht mehr direkt erfolgen, sondern über Factory-Funktionen, damit sie unter der Kontrolle des Frameworks liegt. Dafür gibt es bereits die Funktionen new und get im Datenmodell, die aber noch nicht durchgehen verwendet werden.

Es gibt im Datenmodell viele weiter Funktionen bei denen noch unklar ist, ob sie in Zukunft in ihrer jetztigen Form notwendig sind. Language dient nur der Referenz im Datenmodell. Die Felder für diese Klasse müssen nicht selbstbeschreibent sein (describe). Plugins sind für diese Klasse auch nicht notwendig. Die Rolle von Plugins muss für andere Klassen später durchdacht werden.

Das sind erste Überlegungen. Vermutlich werden sich im Laufe der Umsetzung weitere Schwierigkeiten zeigen, die dann die weitere Planung beeinflussen werden.

Schritte

  1. Initialisierung Doctrine-ORM
  2. Einfache Language-Klasse speichern/laden/ändern/löschen
  3. Ausbau der Klasse, damit sie von der Applikation verwendet werden kann
j3nsch commented 2 years ago

Momentan werden die Properties (Felder) des Modells über Field-Objekte konfiguriert. Bei Language ist das nicht notwendig. Die notwendigen Funktionen sollten vermutlich direkt über set/get-Funktionen implementiert werden oder über entsprechende PHP-Magic. Ich weiß nicht ob sich eine Magic-Implementierung mit Doctrine-ORM kombinieren lässt. Das Mapping mit Doctrine kann wohl über Annotationen, aber auch über eine externe Konfigurationsdatei definiert werden. Vielleicht ließe sich eine externe Konfiguration mit Magic-Methods kombinieren.

Für andere Modelle, die sich mit den Metadaten von Dokumenten befassen, werden wir später auf jeden Fall Magic benötigen. Die wird aber auch die Verwendung von Doctrine etwas anders sein, weil nicht jedes Feld direkt mit einer Spalte in einer Tabelle korrespondieren wird. Das können wir im Augenblick für später lassen.

j3nsch commented 2 years ago

Vielleicht könnten wir die neue Language Klasse bereits jetzt schon in einem anderen Name anlegen, also die existierende Klasse erst einmal nicht ersetzen, um die Entwicklung zu vereinfachen und die alte Klasse als Referenz verfügbar zu haben. Es geht nicht nur um wenige Veränderungen in der Klasse, sondern um eine völlig neue Implementation. Ich bin nicht sicher über die Vor- und Nachteile.

In der Applikation müssten bei direkter Verwendung der Klasse die Use-Statements angepasst werden ohne , im Idealfall, weitere Änderungen im Code. An den Stellen an dem die Klasse anhand ihres vollständigen Namens verwendet wird, z.B. im CRUD-Controller, sollte eine Mapping-Funktion verwendet werden, die vom Framework zur Verfügung gestellt wird, so dass wir nach und nach die alten Klassen durch die neuen Implementation austauschen können, bis das Mapping nicht mehr notwendig ist.

j3nsch commented 2 years ago

Ich alte Language-Klasse hat einige statische Funktionen, die sich auf alle Sprachen beziehen. Diese könnten entweder weiterhin Teil der API der neuen Klasse sein oder in eine Klasse Languages ausgelagert werden.

Egal wie, es geht vor allem darum die Modellklasse Language unabhängig von der Datenbank zu machen und gleichzeitig ihre Nutzung so einfach wie möglich. Ideal wäre es, wenn die neue Klasse ohne Veränderungen in der Application genutzt werden könnte.

Die Modellklasse Language sollte die Funktionen store, delete und auch isUsed haben. Diese Funktionen müssen Calls an ein anderes Objekt weiterleiten, dass die Datenbankoperationen implementiert. Durch Änderung der Konfiguration soll es möglich sein, zwischen der Doctrine-Implementation und z.B. einer einfachen In-Memory Implementation der OPUS 4 Datenbank zu wechseln.

j3nsch commented 2 years ago

Die store und delete Funktionen sollten vermutlich in einer Basisklasse definiert werden, aber auch dort die eigentlich Datenbank-Operation und damit die Abhängigkeit an ein andere Objekt delegieren, dass austauschbar ist. Wenn notwendig, können wir die Instantiierung der Modellklassen kontrollieren, z.B. mit einer new-Funktion, um dann dabei gleich das entsprechende Datenbank-Objekt zu injizieren.