Hallo zusammen,
ich stehe hier vor einem Strukturproblem innerhalb einer MVC-Architektur und möchte gerne wissen, wie ihr folgende Anforderung lösen würdet.
Mein Model besteht aus einem ORM (Eigenentwicklung) auf der Persistenzschicht vom Zend Framework. Das ORM ist eine Ansammlung von Datenobjekten, die die Datenbankstruktur widerspiegeln. Beziehungen werden per Lazy-Loading über die entsprechenden Getter geladen. Und das ist der Knackpunkt.
Ich habe einen komplexen Query, der mir am Ende eine Liste eines bestimmten Objekts zurückliefert. Zu den einzelnen Objekten möchte ich aber noch Zusatzformationen holen, die pro Objekt über eine Beziehungstiefe von 2 oder mehr abbildbar sind. Diese Infos will ich
jedesmal holen, wenn dieses Objekt - sei es als Liste oder einzeln - geladen wird. Hier Lazy-Loading einzusetzen wäre natürlich Unsinn. Also bleibt meiner Meinung nach nur Preloading. Natürlich kann ich in jedem formulierten Query für das Hauptobjekt einfach Joins dranhängen und am Ende in eine Objektstruktur bringen, aber es soll transparent geschehen, damit der Programmierer das nur einmal definieren muss.
Wie könnte eine Lösung dafür aussehen?
Ein weiteres Problem dabei ist, diese Zusatzinfos zu verpacken und an die View weiterzureichen. Eine Möglichkeit wäre, Transferobjekte zwischen Model und View bzw. Controller einzuführen, die dann die Struktur für die View abbilden. Dazu bräuchte man wieder ein Mapping und muss mehrere Schichten pflegen. Man könnte auch die komplette Beziehungstiefe in den Datenobjekten beibehalten, damit wäre das Auslesen der Daten in der View etwas aufwändiger.
Wie macht ihr das? Ist es valide, an die Datenobjekte Propertys dranzuhängen, die nichts mit dem Objekt direkt zu tun haben?
Ich hoffe, ich habe mich klar ausgedrückt. Ansonsten einfach nachfragen.
Vielen Dank!
Da noch niemand von den rund 55 Besuchern geantwortet hat, versuche ich es mal mit meinem theoretischen Ansatz für das erste Problem:
Es existieren folgende Datenbanktabellen:
Code:
CREATE TABLE `translation` (
`translation_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`source_word_id` int(10) unsigned NOT NULL,
`target_word_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`translation_id`),
UNIQUE KEY `unique_translation` (`source_word_id`,`target_word_id`),
KEY `translation_target_word_id_word_word_id` (`target_word_id`),
CONSTRAINT `translation_source_word_id_word_word_id` FOREIGN KEY (`source_word_id`) REFERENCES `word` (`word_id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `translation_target_word_id_word_word_id` FOREIGN KEY (`target_word_id`) REFERENCES `word` (`word_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3412849 DEFAULT CHARSET=utf8;
CREATE TABLE `word` (
`word_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`word` varchar(255) NOT NULL,
`language_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`word_id`),
UNIQUE KEY `word_2` (`word`,`language_id`),
KEY `language_id` (`language_id`),
CONSTRAINT `word_ibfk_2` FOREIGN KEY (`language_id`) REFERENCES `language` (`language_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=574117 DEFAULT CHARSET=utf8;
Man beachte die beiden Fremdschlüssel der Tabelle
translation auf die Tabelle
word. Für jede dieser Tabellen gibt es ein entsprechendes Datenobjekt. Per magischer Getter kann ich in einem Translation-Objekt auf Source-Word und Target-Word zugreifen, die dann nachgeladen werden.
Problem dabei: Beim Zugriff auf diese Beziehungen im Falle einer großen Liste von Translation-Objekten habe ich zuviele einzelne Datenbankabfragen.
Das möchte ich optimieren, indem das ORM automatisch (konfigurierbar) sämtliche Beziehungen in das Query reinjoint und anschließend die Objekte zusammenbastelt. In diesem Fall würde ich also zweimal die Tabelle
word joinen. Das wiederum ergibt ein Resultset, in dem sich die gleichnamigen Spaltennamen überschreiben, d.h. ich bekomme entweder nur die Daten für das Source-Word oder das Target-Word. Leider gibt es (in mysql) keine Namespaces dafür.
Wenn man das ganze weiterspinnt und automatisch eine
beliebige Tiefe von Beziehungen laden will, z.b. noch die Language im Word-Objekt, wird's noch ekliger. Um es noch schlimmer zu machen, könnte man auch gleich noch die 1:n-Beziehungen holen, z.b.
Code:
Translation
->Source-Word
->Alle Translations mit diesem Source-Word
. Wie man sieht, könnte das zu Rekursionen führen.
Mir fällt da nichts schöneres ein, als die Joins mit kryptischen Aliasen zu versehen und eine Art Mapping mitzuschleifen, damit ich nachher die Daten den Objekten zuordnen kann. Das ist jedoch nicht einfach umzusetzen, außerdem kann so ein Query dann ziemlich groß werden. Evtl. gibt es sogar performantere Lösungen.
Hat damit jemand Erfahrung?