SQL-Statements, verschiedene Module
Christian Seiler
- datenbank
Hallo,
Ich schreibe gerade an einer modularen Software. Diese soll unter anderem ein Gästebuch und eine Bildergalerie enthalten. Ich habe also prinzipiell 2 Module: Gästebuch, Bildergalerie. Diese Module habe ich weiter aufgeteilt, dass ich pro Modul folgende Dinge habe: Datenbankzugriffsklasse, Programmlogik und Template.
Mein Problem ist nun folgendes:
Ich habe vor, die Kommentare selbst über die gleiche Klasse, wie das Gästebuch, abzuhandeln. Soweit noch kein Thema. Ich möchte nun jedoch bei der Auflistung von den Bildern (inkl. Thumbnails) noch dazuschreiben, wie viele Kommentare dabei sind. Wie ich sehe habe ich da drei Möglichkeiten:
1. Ich pfeiffe auf die saubere Trennung und baue mir in den SELECT auf die Bildertabelle noch zusätzlich (ganz pragmatisch) die Abfrage über die Anzahl der Kommentare ein. Hätte den Vorteil, dass es schnell geht; hätte den Nachteil, dass da nix mehr von wegen sauberer Trennung wäre. (unelegant, doof)
2. Mir ist Geschwindigkeit egal und ich hole mir dann einfach in der Programmlogik [1] einzeln für jedes Bild die Anzahl der Kommentare. (langsam, unelegant, doof)
3. Ich schaffe eine Art "Interface" zwischen den beiden Klassen, bei dem die eine Klasse die andere Klasse zu Hilfe nehmen kann, wenn der SELECT zusammengebaut wird. Will heißen: Die Funktion 'getList' der Klasse (nennen wir sie mal hier) 'GalerieBild' bekommt ein Objekt der Klasse 'Kommentar' mitgeliefert. Dieses Objekt wird dann verwendet, um den SELECT vollends zusammenzubasteln. Und genau da bleibe ich stecken. Hat da jemand eine Idee, wie das realisiert werden kann?
Achja, Galerie und Kommentarfunktion sind mein aktuelles Problem, ich suche natürlich nach einer generischen Lösung. Will heißen: Ich möchte das ganze auch noch ausdehnen, dass ich im Prinzip auch noch die Benutzerklasse mit einbauen kann: Dass ich also beim SELECT gleich auch noch die Daten über den Benutzer, der das Bild eingestellt hat, mit hole. Die Möglichkeiten eines derartigen Interfaces wären grenzenlos. (daher suche ich auch nach einer Idee für so etwas)
Noch etwas: Es muss mit MySQL 3 laufen.
Viele Grüße,
Christian
[1] Die durchaus etwas von den unterschiedlichen Zugriffsklassen wissen darf - ich möchte *ausschließlich* innerhalb einer Ebene trennen!
Hallo Christian,
Mein Problem ist nun folgendes:
...
3. Ich schaffe eine Art "Interface" zwischen den beiden Klassen, bei dem die eine Klasse die andere Klasse zu Hilfe nehmen kann, wenn der SELECT zusammengebaut wird. Will heißen: Die Funktion 'getList' der Klasse (nennen wir sie mal hier) 'GalerieBild' bekommt ein Objekt der Klasse 'Kommentar' mitgeliefert. Dieses Objekt wird dann verwendet, um den SELECT vollends zusammenzubasteln. Und genau da bleibe ich stecken. Hat da jemand eine Idee, wie das realisiert werden kann?
äh... ??? - in dem Gästebuch stehen Kommentare, die eindeutig einem Bild zugeordnet sind oder was? Also nur mal so allgemein: in meinem derzeitigen Projekt, meinem Forum habe ich - glaube ich - sowas ähnliches gemacht, wie Du beschreibst: Klassen enthalten als Eigenschaften Objekte von anderen Klassen und schon kann ich auf deren Eigenschaften zugreifen. Z.B. ist bei mir die Thread-Liste ein Objekt, das die einzelnen Threads als Objekte enthält und die wiederum enthalten die Postings als Objekte. So habe ich in jedem Posting zum Beispiel auch die Information, wieviele Postings der ganze Thread enthält, oder ob er gerade auf oder zugeklappt ist usf. Aber vielleicht rede ich ja auch an Deinem Problem vorbei... ;)
Gruß, Andreas
Hallo Andreas,
in dem Gästebuch stehen Kommentare, die eindeutig einem Bild zugeordnet sind oder was?
Nein. Aber ich verwende das gleiche Datenmodell für Gästebuch und Kommentare zu einem Bild.
Aber vielleicht rede ich ja auch an Deinem Problem vorbei... ;)
Ja, leider...
Ich machs nochmal deutlich:
Ich hab folgende Tabellen:
bilder
- bild_id
- breite
- hoehe
- beschreibung
- ...
- galerie_id
kommentare
- kommentar_id
- typ
- ref_id
- autor
- kommentar
Beim Gästebuch ist in der Kommentartabelle typ = 0 und ref_id = 0. Bei der Bildergalerie ist in der Kommentartabelle typ = 1 und ref_id = id des Bilds. Bei anderen, zukünftigen Dingen, könnte ich mir vorstellen, dass typ = 2 und ref_id = andereid ist, wenn ich nunmal andere Dinge kommentieren will.
Jetzt habe ich eine Klasse GalerieBild, mit der ich auf die Tabelle 'bilder' zugreifen kann. Die bietet zum Beispiel die Funktion getList an. Diese macht nichts anderes, als folgenden SELECT auf die Datenbank abzusetzen und das Ergebnis zurückzuliefern:
SELECT bild_id, breite, hoehe, beschreibung, ..., galerie_id FROM bilder WHERE galerie_id = X ORDER BY irgendwas ;
Wobei X die aktuell gewählte Galerie ist.
Ich habe nun eine Klasse Kommentar, mit der ich auf die Tabelle 'kommentare' zugreifen kann. Diese bietet auch eine Funktion getList an, die halt eine Liste von Kommentaren zurückliefert. Außerdem bietet diese eine Funktion getCount an, die die Anzahl Kommentare zurückliefert:
SELECT COUNT(kommentar_id) FROM kommentare WHERE typ = X AND ref_id = Y
Wobei typ in diesem Fall 1 wäre (Galerie) und ref_id die ID des Bildes.
Wenn ich nun auf der Galerieübersicht zu jedem Bild die Anzahl an Kommentare zurückliefern will, habe ich 3 Optionen (nochmal):
1. In der Klasse 'bilder' das ganze mit reinpfrimeln:
SELECT bild_id, ..., galerie_id, COUNT(kommentar_id) FROM bilder LEFT JOIN kommentare ON kommentare.typ = 1 AND kommentare.ref_id = bilder.bild_id GROUP BY bild_id, ..., galerie_id ORDER BY irgendwas ;
2. In einer Schleife die Funktion getCount der Klasse Kommentare für jedes einzelne Bild aufrufen.
3. Eine Schnittstelle zwischen beiden Klassen herstellen, mit der dann der SELECT aus 1. dynamisch zusammengebastelt wird, ohne, dass die beiden Klassen voneinander und von den unabhängigen Datenstrukturen wissen.
Und zu 3 suche ich Anregungen.
Viele Grüße,
Christian
Hallo Christian,
soweit ich das aus Deiner Beschreibung deuten kann, steckst Du in einem selbsterzeugten Dilemma. Es entsteht dadurch, daß Du keine mehrschichtige Architektur verwendest, sondern Deine Datenzugriffe in die Objekte der Geschäftsprozesslogik GPL reingeknusselt hast. Hättest Du eine saubere Trennung zwischen GPL und Datenhaltung, dann würde zum Beispiel Dein Galerieobjekt eine Kollektion von Bildobjekten besitzen, die ihrerseits eine Kollektion von Kommentarobjekten enthalten. Die Frage nach dem Bestücken der Objekte mit ihren Daten aus der DB wird nicht innerhalb der Objekte beantwortet, sondern durch entsprechende Dialoge zwischen der GPL an und der Datenhaltungsschicht.
- In der Klasse 'bilder' das ganze mit reinpfrimeln: [...]
- In einer Schleife die Funktion getCount der Klasse Kommentare für jedes einzelne Bild aufrufen.
- Eine Schnittstelle zwischen beiden Klassen herstellen, mit der dann der SELECT aus 1. dynamisch zusammengebastelt wird, ohne, dass die beiden Klassen voneinander und von den unabhängigen Datenstrukturen wissen.
Wenn Dein Dilemma tatsächlich so ist wie von mir gesehen, dann halte ich moralische Skrupel gegen Deine Überlegung Nr. 1 für fragwürdig; Du hast ohnehin eine fundamentale Trennwand eingerissen. Wenn Du dennoch den Anschein eines sauberen Modells waren möchtest, so käme Nr. 2 in Betracht (wahrscheinlich wird der Performancelohn, den Du zu zahlen hast, sich in Grenzen halten). Nr. 3 würde mir die Komplexität des Gesamtmodells zu sehr erhöhen und zukünftige Wartung und Erweiterung erschweren - dann käme vielleicht eher ein Umbau auf ein mehrschichtiges Modell in Betracht.
HTH Robert
Hallo Robert,
...Hättest Du eine saubere Trennung zwischen GPL und Datenhaltung, dann würde zum Beispiel Dein Galerieobjekt eine Kollektion von Bildobjekten besitzen, die ihrerseits eine Kollektion von Kommentarobjekten enthalten.
also doch so, wie ich beschrieben habe gell? Ich habe es auch so verstanden, daß er die Daten für das Gästebuch und für die Bilderkommentare in der gleichen Tabelle hält. Ist das nicht seltsam?
Gruß, Andreas
Hallo Andreas,
also doch so, wie ich beschrieben habe gell? Ich habe es auch so verstanden, daß er die Daten für das Gästebuch und für die Bilderkommentare in der gleichen Tabelle hält. Ist das nicht seltsam?
ich sehe diesen Punkt nicht als das Problem von Christian an; seltsam finde ich es schon gar nicht.
Christians Problem liegt darin, daß in seiner Architektur keine getrennte Datenhaltungsschicht enthalten ist und er sich jetzt mit der Frage plagt, daß seine Zugriffe auf die DB anscheinend die Kapselung seiner Objekte sprengt und er das gerne verhindern möchte.
HTH Robert
Hallo Robert,
Vorneweg: Ich spreche jetzt lieber mal von Programmlogik und nicht von GPL, da das ganze kein kommerzielles Projekt ist. Ich hoffe, wir meinen das gleiche.
(außerdem verwende ich PHP, falls das von Interesse ist)
Es entsteht dadurch, daß Du keine mehrschichtige Architektur verwendest, sondern Deine Datenzugriffe in die Objekte der Geschäftsprozesslogik GPL reingeknusselt hast.
Ich verstehe nicht ganz, wie Du das meinst. Ich habe durchaus verschiedene Schichten. Ich habe eine Schicht, die den kompletten Datenbankzugriff regelt. Dazu zählen die Klassen 'GalerieBild' und 'Kommentar'. Diese regeln den kompletten Datenzugriff. Dort ist *nichts* an Programmlogik enthalten.
In der Programmlogik verwende ich dann die Objekte und deren Methoden, um auf den Datenbestand zuzugreifen.
Also (vereinfacht):
$liste = $galerie_bild->getList ($aktuelle_galerie);
$liste sieht dann so aus:
array (
array (
'bild_id' => 4,
'beschreibung' => 'Foto von mir',
...
),
array (
'bild_id' => 5,
'beschreibung' => 'Foto von mir',
...
),
...
)
Oder auch:
$res = $galerie_bild->add ($hoehe, $breite, $beschreibung, ...);
Hättest Du eine saubere Trennung zwischen GPL und Datenhaltung, dann würde zum Beispiel Dein Galerieobjekt eine Kollektion von Bildobjekten besitzen, die ihrerseits eine Kollektion von Kommentarobjekten enthalten. Die Frage nach dem Bestücken der Objekte mit ihren Daten aus der DB wird nicht innerhalb der Objekte beantwortet, sondern durch entsprechende Dialoge zwischen der GPL an und der Datenhaltungsschicht.
Könntest Du mir mal ein konkretes Beispiel dafür liefern? (Oder einen Link?)
Viele Grüße,
Christian
HI!
Könntest Du mir mal ein konkretes Beispiel dafür liefern? (Oder einen Link?)
Hat zwar nix hiermit zu tun, aber interessant zum Thema wären vielleicht folgende Artikel:
http://www.phppatterns.com/index.php/article/articleview/49/1/1/
http://www.phppatterns.com/index.php/article/articleview/25/1/1/
Grüße
Andreas
Hallo!
- Ich pfeiffe auf die saubere Trennung und baue mir in den SELECT auf die Bildertabelle noch zusätzlich (ganz pragmatisch) die Abfrage über die Anzahl der Kommentare ein. Hätte den Vorteil, dass es schnell geht; hätte den Nachteil, dass da nix mehr von wegen sauberer Trennung wäre. (unelegant, doof)
ja, das ist doof.
- Mir ist Geschwindigkeit egal und ich hole mir dann einfach in der Programmlogik [1] einzeln für jedes Bild die Anzahl der Kommentare. (langsam, unelegant, doof)
ja, auch doof ;-)
- Ich schaffe eine Art "Interface" zwischen den beiden Klassen, bei dem die eine Klasse die andere Klasse zu Hilfe nehmen kann, wenn der SELECT zusammengebaut wird. Will heißen: Die Funktion 'getList' der Klasse (nennen wir sie mal hier) 'GalerieBild' bekommt ein Objekt der Klasse 'Kommentar' mitgeliefert. Dieses Objekt wird dann verwendet, um den SELECT vollends zusammenzubasteln. Und genau da bleibe ich stecken. Hat da jemand eine Idee, wie das realisiert werden kann?
Ich würde hierfür einfach eine Deine Datenbankzugriffsklasse verwenden. Was genau soll diese Klasse in Deinem Konzept machen?
Wie genau holst Du Dir in Deiner Programierlogik die Liste mit den Bildern? Ich würde es als Array machen. Also so eine Methode wie fetchImageList(), die dann wie in Deinem 1. Vorschlag die Bilder aus der Db holt, und als ordneltich formatierten Arrray zurückgibt, eben mit den Kommentaren.
Du könntest auch eine Klasse "Bild" verwenden, die als Eigenschaft die Anzahl der Kommentare hat. Du könntest die Bilder dann über einen Array $bilderListe mit den einzelnen Bild-Objekten als Elemente speichern. Wenn Du sowas machst, kannst Du das entweder direkt in der Programmierlogik machen, oder über eine abstrakte Klasse, die dann mit Hilfe der Datenbankzugriffsklasse und die Bild-Objekte erzeugtm und den Array mit den Objekten zurückgibt. Du rufst also auf
$bilderListe = BilderListe::getList();
Diese getList() Methode könnte dann über eine Methode der Datenbankzugriffsklasse, vielleicht auf getList() eine Liste als Array bekommen, und würde die dann in einer Schleife einen Array mit Bild-Objekten erzeugen, indem sie die jeweiligen set*() Methoden der Bild-Klasse verwendet.
Gut, das könnte auch die Datenbankzugriffsklasse machen, aber eigentlich solte die nichst von den anderen Objekten wissen.
Ist alles eine Frage wie komplex das ganze wird/werden soll.
Achja, Galerie und Kommentarfunktion sind mein aktuelles Problem, ich suche natürlich nach einer generischen Lösung. Will heißen: Ich möchte das ganze auch noch ausdehnen, dass ich im Prinzip auch noch die Benutzerklasse mit einbauen kann: Dass ich also beim SELECT gleich auch noch die Daten über den Benutzer, der das Bild eingestellt hat, mit hole. Die Möglichkeiten eines derartigen Interfaces wären grenzenlos. (daher suche ich auch nach einer Idee für so etwas)
Gut, diese Information hast Du ja in der Tabelle gespeichert, wäre also ein leichtes die mit auszulesen und als Eigenschaft der Bild-Klasse zu verwenden. Dann bräuchtest Du eigentlich nich eine Klasse "Benutzer", in der Du die Daten der Benutzers speicherst.
Wenn Du dann von dem den Namen auslesen willst, dann könntest Du das ebenfalls in der gleichen Query machen wie oben, und den Namen als Eigenschaft in die Bild Klasse schreiben, aber das halte ich nicht für so Sinnvoll. Ich würde eher eine Referenz auf das Benutzer-Objekt in der Bild-Klasse speichern.
Ich persönlich halte es schon für sinnvoll eine abstrakte Klassenmethode zu verwenden, um eine ganze Reihe von Objekten zu erzeugen. Das ist halt immer ein Trade-Off, ein extrem sauberes Design kostet meist Performance. Du könntest aber auch eine abstrakte Klasse "Galierie" verwenden, und darin eine Methode aufrufen, die einen Array aus der Datenbankzugriffsklasse, welche dann eine otpimierte SQL-Abfrage verwendet, erhält, und entsprechend Bilder und Benutzer-Objekte erzeugt. Du musst die Objekte ja nicht immer vollständig füllen, sondern nur mit den Eigenschaften, die Du brauchst.
[1] Die durchaus etwas von den unterschiedlichen Zugriffsklassen wissen darf - ich möchte *ausschließlich* innerhalb einer Ebene trennen!
Das verstehe ich jetzt nicht. Was ist denn "eine Ebene"?
Grüße
Andreas
Andreas Korthaus
Wie genau holst Du Dir in Deiner Programierlogik die Liste mit den Bildern? Ich würde es als Array machen.
Tue ich auch.
Also so eine Methode wie fetchImageList(), die dann wie in Deinem 1. Vorschlag die Bilder aus der Db holt, und als ordneltich formatierten Arrray zurückgibt, eben mit den Kommentaren.
Ok, genau das will ich.
[...]
Diese getList() Methode könnte dann über eine Methode der Datenbankzugriffsklasse, vielleicht auf getList() eine Liste als Array bekommen, und würde die dann in einer Schleife einen Array mit Bild-Objekten erzeugen, indem sie die jeweiligen set*() Methoden der Bild-Klasse verwendet.
Ja gut, aber das entspräche ja in abgewandelter Form meinem Vorschlag 2: »Ich pfeiff auf die Performance und hol mir die Eigenschaften für jedes Bild einzeln.« Oder verstehe ich Dich da falsch?
Viele Grüße,
Christian
Hi!
Diese getList() Methode könnte dann über eine Methode der Datenbankzugriffsklasse, vielleicht auf getList() eine Liste als Array bekommen, und würde die dann in einer Schleife einen Array mit Bild-Objekten erzeugen, indem sie die jeweiligen set*() Methoden der Bild-Klasse verwendet.
Ja gut, aber das entspräche ja in abgewandelter Form meinem Vorschlag 2: »Ich pfeiff auf die Performance und hol mir die Eigenschaften für jedes Bild einzeln.« Oder verstehe ich Dich da falsch?
Nein, an die DB geht eine einzige Query, nur werden aus dem Ergebnis dann von einer eigenen Klasse die Objekte erzeugt.
so in etwas
$galerie_objekt = GalerieManager::new();
// GalerieManager::new() macht sowas:
$galerie_obj =& new Galerie;
$res = $db->query ("SELECT....");
foreach ($tmp = $res->fetch...) {
$bild_objekt =& new Bild;
$bild_objekt->setDescription($tmp['desc']);
$bild_objekt->setUrl($tmp['url']);
$bild_objekt->setHeight($tmp['height']);
$bild_objekt->setComments($tmp['com']);
$galerie_obj->addPicture(&$bild_objekt);
}
return $galerie_obj;
Das ist sehr grob, geht nur ums Prinzip. Ist jetzt etwas abgewandelt, aber ich glaube so ungefähr würde ich es machen.
So ganz toll wird es nicht gehen, da Du Du bei der Abfrage nunmal Objekt-übergreifendes Wissen brauchst, wenn Du das mit einer einzigen machen willst. Ich würde das wo geht abstrakte Manager-Klassen machen lassen, die die Kommunikation zur DB und das schreiben in die eigentlichen Objekte der Business-Logik schreiben.
Und dann musst Du sehen, wenn Dir das an einer Stelle fürchterlich unperformant vorkommt, dann würde ich das so machen wie hier, eine Klasse die das ganze zusammenfasst und eine Manager-Klasse die halt etwas über mehr als eine Tabelle wissen muss.
Grüße
Andreas