Sven Rautenberg: DAO / MVC-Design in (PHP-)Webapplikationen - sinnvoll?

Beitrag lesen

Moin!

Ich will das Posting mal mit einer Geschichte beginnen. :)

Ich habe gerade mal wieder in den Quelltext eines ungefähr ein Jahr alten PHP-Formmailers reingeschaut, den ich geschrieben hatte. Das Skript ist wirklich nicht schlecht, es hat eine einfache Template-Engine, ist gesichert gegen Spam-Attacken (mailt als nicht wild in der Gegend umher, sondern nur an definierte Adressen), garantiert auch, dass keine Formularfelder verloren gehen - und wenn alle Stricke reißen, schickt es eben einfach nur einen Dump des Formulars weg.

Aber: Das Skript basiert komplett auf Funktionen, die direkt in der einzigen Datei enthalten sind, es ist irgendwie unübersichtlich und gefällt mir rein ästhetisch jetzt nicht mehr. Aber es macht genau, was es soll.

Jetzt habe ich unlängst einen Webshop geschrieben. Getreu dem Motto "beim nächsten Shop ist alles anders" wollte ich hierbei endlich mal konsequent auf Klassen setzen. Und es hat sich IMO gelohnt, weil dadurch viele Dinge einfacher werden, weil sie tatsächlich voll gekapselt sind. Es gibt eine Klasse, die für Datenbankkommunikation zuständig ist. Diese wird (in einer separaten Datei, der Übersichtlichkeit wegen) erweitert um eine Klasse, die für die Session-Funktionen zuständig ist. Außerdem gibts (von extern angeheiratet) eine Klasse für die Templates und eine zum Mailen.

Erst diese Templateorientierung macht es jetzt wirklich möglich, dass ich im Shop prinzipiell total mit virtuellen URLs operieren kann. mod_rewrite ist toll dafür. Und erst dieses Operieren mit virtuellen URLs löst mich von der bisherigen Denkweise, dass eine URL gleich einem Skript ist, und das Skript dann bitteschön die Datenbank abfragen möge, den Preis (womöglich an drei verschiedenen Stellen unterschiedlich) berechnet und dann, noch direkt im Skript, die Ausgabe einer Seite vornimmt.

Ich mache zur Zeit eine Art Mischmasch, ich verwende ein Script welches alle Libs und Einstellungen läd, welches per Mod-Rewrite bei jedem Request geladen wird und entsprechend dem eigentlichen Request dann ein Modul(Verzeichnis) und dort ein Script läd, ein Request sieht etwas so aus:

/[modulname]/[scriptname]

und alles weiter kommt dahinter als Parameter. (Wobei ich auf mod_rewrite aufgrund der Größe des Moduls lieber verzichten würde...)

So in der Art arbeite ich derzeit im Prinzip auch (mag ja sein, dass im Laufe der Zeit und mit der Erfahrung sich das noch weiter in Richtung dem "richtigen" Programmieren ändert). Allerdings regelt mod_rewrite bei mir im Vorfelde schon ein paar Kleinigkeiten. Nur direkt im Shopbereich wird die volle Shopfunktionalität benötigt. Außerhalb des Shops wird zwar per Template eine Warenkorb-Zusammenfassung gezeigt, aber das geschieht mit einem wesentlich leichteren Skript, welches nur eben die Session-Daten dort einträgt, und ansonsten nur das gewünschte Seiten-Template durchnudelt.

Das Shop-Skript selbst ist wiederum schön aufgeteilt in Unterskripte, die zwar alle per include eingebunden sind, aber in ihrer Gesamtheit den Aufgabenverteiler unübersichtlich machen würden.

So kann ich mich jeweils der Datei zuwenden, an deren Aufgabenabarbeitung ich etwas ändern möchte, oder ich nehme mir den Aufgabenverteiler vor, der anhand der angeforderten URL zum richtigen Modul weiterleitet.

Für mich sieht es rein logisch jedenfalls im Moment so aus, dass der Webserver im Prinzip wirklich nur datendurchleitende Funktion hat. Alle Requests kommen zentral an einem Skript an, welches seinerseits die Ablauflogik implementiert hat.

Diese Ebene greift dafür auf die einzelnen Klassen (DB, Session, Template, Mail) zu, um Mid- bis Low-Level-Operationen durchzuführen. Das Interface ist standardisiert. Die Klassen regeln dann die tatsächliche Ausführung, typischerweise durch ein paar als privat anzusehende (aber in PHP 4 ja nicht als solche deklarierbaren) Methoden, die intern aufgerufen werden.

Das Script was dann geladen wird weiß was zu tun ist und läd dann notwendige Klassen und "macht dann irgendwas" damit.

Das ist in meinen Augen sinnvoll.

Im Grund genommen zerfällt eine größere Anwendung ja typischerweise in mehrere Schichten: Präsentation gegenüber dem User, Verarbeitung und Datenspeicherung. Wobei es natürlich noch Zwischenschichten oder Dopplungen geben kann.

Bei der typischen Anfänger-PHP-Programmierung öffnet jedes Skript einzeln die Datenbankverbindung, um etwas zu speichern oder zu lesen, rödelt dann mit der CPU rum, um was auszurechnen, und gibt schließlich unter Aufwendung vieler ECHOs oder PRINTs ein Ergebnis bekannt. Ach ja, was vergaß ich: Am Anfang werden natürlich auch noch irgendwelche schönen POST- oder GET-Werte einbezogen.

Das Problem: Die Präsentationsschicht hört nicht nach dem ECHO auf, sondern im Gegenteil beginnt dort erst, denn nach dem ECHO erfolgt die Auslieferung an den Webbrowser - und _der_ sorgt für die Darstellung und "Präsentation".

Indem ich mich von der klassischen Festlegung "URL=Skript" löse, kann ich mich auch von der Gedankenfalle "ECHO ist die Präsentation" lösen. Das PHP-Skript hat dann gerade nicht mehr die Aufgabe, für jedes kleine Fitzelchen Darstellung auf dem Browser zu sorgen, sondern es hat die Aufgabe, die Template-Engine mit den Daten zu versorgen, die angezeigt werden sollen. Auf diese Weise verschmelzen Template-Engine und Browser zu einer logischen Einheit, die ich durch die Präsentationsschicht "Template-Datei" programmieren kann, ohne mich zu diesem Zeitpunkt auf die zugrundeliegende Berechnungslogik oder Datenquelle konzentrieren zu müssen.

Genau das Gleiche geschieht auch mit der Datenbank-Klasse. Indem ich einfach nur standardisiert die Methode "hole Artikelliste" abfrage und ein Array der gefundenen Artikel mit allen gespeicherten Eigenschaften zurückerhalte, werde ich von für Anfänger verlockenden Möglichkeiten mit direkt codeausgebenden while(mysql_fetch_array())-Schleifen ferngehalten.

Im Gegenteil kann ich, weil es ja nur ein Array gibt, auf dieselbe Weise eben einmal eine Artikelliste einer Artikelkategorie holen, und ein anderes Mal eine Artikelliste, nach der ein Suchbegriff suchen soll.

Und ich kann andererseits in der DB-Klasse beispielsweise die Artikelsuche vom simplen FULLTEXT-Index umstellen auf einen Full-Table-Scan "LIKE %suchwort%", oder auch komplexe Indexing-Mechanismen einbauen. Resultat ist immer eine Artikelliste, die ich ausgebe - ohne irgendeine Änderung an Code aus anderen Schichten.

Man könnte zwar auch per php.ini per auto-prepend(oder wie das heißt) ein Script  vor jedem Request laden, welches dann die Konfigurationen... durchführt, also dass über den Webserver immer echte Pfade aufgerufen werden, nur ist das dann etwas schwierig wenn die ganzen Plugins mit allen Unterverzeichnissen innerhalb es doc-root liegen, das mag ich z.B. nicht so.

Ja, sowas könnte man machen. Würde vielleicht sogar gut funktionieren. Dennoch muß ja bei so einer Konstruktion für jedes per URL aufgerufene Skript irgendetwas auf dem Server vorhanden sein - sei es das tatsächliche Skript, oder irgendein symbolischer Link woandershin, oder eine Serverkonfiguration, um es so umzubiegen.

Da habe ich das Umbiegen doch irgendwie gerne selbst in der Hand. :)

- Sven Rautenberg

--
"Beim Stuff für's Web gibts kein Material, was sonst das Zeugs ist, aus dem die Sachen sind."
(fastix®, 13. Oktober 2003, 02:26 Uhr -> </archiv/2003/10/60137/#m338340>)