Hallo!
Meinst Du richtig mit OO Analyse, Entwurf, schön alles mit UML in Objekten/Klassen modelliert? Um Gottes Willen, es geht um einen Webshop, keine Doktorarbeit. :)
Gut bei kleineren Projekten vielleicht "unnötiger Overhead", aber das ist schon gut, wenn man sich vorher diese Gedanken macht bekommt man eigentlich leichter an ein gut durchdachtes, modulares Design.
Ich habe zwar 394 Zeilen CSS ;) ...
Man sollte nur keine "Business-Logik" in CSS auslagern ;-)
Ich verwende keine gesonderte Datenbank-Abstraktion. Das leistet in gewissem Sinne meine DB-Klasse.
Ja, meist braucht man das auch nicht, ich verwende zur Zeit PEAR::DB, aber ich überlege noch ob es vielleicht Sinn macht ADODB oder PEAR::MDB zu verwenden, ich habe das zum Glück direkt so angelegt dass der Wechsel möglich wäre, wenn das auch nicht alles so glatt laufen dürfte, aber möglich wäre es.
Ich sehe das so: Jede Datenbank hat ihren eigenen SQL-Dialekt.
Den versuche ich zu vermeiden - soweit es geht, und beschränkle mich da lieber und mache mehr mit PHP. Denn bei mir gehört die Möglichkeit mit verschiedenen RDBMS zu arbeiten mit zum "must-have".
Und irgendwann in der Abstraktionsreihe wird es dazu kommen, dass die gewünschte Anforderung in SQL übersetzt werden soll.
Es gibt ja viele fertige, wie die von mir genannten, sogar als PHP-Modul "dbx" (von wegen PHP hätte keine DB-Abstraktion, sowohl DBX als auch PEAR gehören zum Standardumfang).
Alternativ kann ich für gewisse Datenbanken fehlende Funktionalität natürlich in Code emulieren. Mircosofts MSSQL kann kein "LIMIT anzahl,offset", sondern nur "TOP anzahl"? OK, dann wird der Offset zur Anzahl draufgerechnet und intern die ersten $offset Ergebnisse weggeworfen. Blöd für die Datenbank, aber gut für die Abstraktion. :)
Limits können viele Abstraktionsschichten emulieren, ebenso auto-increment und vieles andere. Problematischer wird es mit einigen "krasseren" Unterschieden wie JOIN, das gibts wohl nicht in älteren Oracle Dialekten. Und das ist ein großes Problem, daher muss ich an manchen Stellen selber zusätzliech Querys schreiben, Datenbank-Abhängig.
Was heißt "erweitert"? Doch keine Vererbungs-Beziehung, oder?
Doch. Ich hatte im ersten Ansatz festgestellt, dass die Session-Klasse DB-Zugriffe benötigt, beispielsweise, um den Preis von Produkten zu erfahren, die in den Warenkorb gelegt werden. Meine erste Lösung dazu war, eine Instanz der DB-Klasse und eine Instanz der Session-Klasse zu haben und die DB-Instanz jeder Session-Methode als Parameter mitzugeben. Das fand ich dann aber tierisch umständlich, und so include ich die DB-Klasse in der Session-Klasse, erweitere die DB-Klasse um Session-Methoden und benutze dann nur die Session-Instanz im Shop. Ja, mag etwas böse erscheinen, fand ich aber gut so.
Hm, also das machen einige Leute so, aber ich finde das hat dann noch wenig mit OOP zu tun. Dadurch dass Du die DB-Klasse direkt verwendest, ist dessen Implementierung nicht mehr unabhängig von der Session-Klasse. Das mag in Deinem Fall auch ziemlich egal sein, bei mit nicht. Aber wo besteht das Problem entweder im Konstruktor der Session-Klasse das DB-Objekt in die Session-Klasse zu übernehmen, entweder per $this->db = $_GLOBALS['DB']; oder als Parameter an den Konstruktor übergeben?
Ich verwende zur Zeit global $db in den Methoden die DB-Zugriff brauchen. Andere Frage - wozu hast Du eine Session-Klasse? Was macht die?
| Außerdem eröffnet mir diese Trennung eben auch Dinge wie einen XML-Datenbankimport, der logischerweise auf die DB-Klasse zugreift, aber keine Session-Funktionalität benötigt.
Das man es trennen soll ist klar, aber nicht durch Vererbung 2er vollkommen verschiedener Objekte nur der Bequemlichkeit halber wieder verbinden ;-)
Ich verwende hierfür jeweils Smarty. Das ist hervorragend geeignet. Ich verwende vlibTemplate. Das funktioniert auch hervorragend.
Ich habe letztens irgendwo eine Sammlung der Template-Engines für PHP gesehen, waren weit über 50, danach habe ich nicht mehr geguckt ;-)
Was sind denn "Virtuelle URLs"? Meinst Du also keine direkte Zuordnung auf Scripte? Aber was genau hat das mit den Templates zu tun? Mit virtuellen URLs meine ich das, was durch RewriteRule ^/shop/.+.html$ /backoffice/shop.php
entsteht. Für sämtliche .html-Adressen im Shop ist genau ein Skript zuständig, aber ohne, dass dieses irgendwie auffällt oder nach außen sichtbar wird.
Und den Namen benutzt Du nicht als Parameter in Deinen Scripten? Oder suchst Du den in PHP nochmal aus dem Request-String?
Sämtliche Formular-POSTs und -GETs gehen nicht an dieses Skript, sondern (nach dem Prinzip "Affenformular") immer an sich selbst.
Aber Du brauchst doch Parameter oder Formulare in Deinem Shop, oder nicht?
Wie erwähnt: Die RewriteRule ist nicht die einzige, die zum Einsatz kommt, das geht etwas nach Bereichen getrennt. Der Apache spielt also als Schicht auch eine Rolle (wenngleich die Schicht dünn und unscheinbar ist).
Bei mir geht alles in ein Script, welches alle notwendigen Daten läd, und anhand des originalen Requests genau weiß was zu tun ist, also es wird immer ein Script mit dem angegebene Namen aus dem angegebenen Modul-Verzeichnis geladen. Dieses Script enthält einen kleinen Controller der entsprechend den Parametern weitere Aktionen durchführt.
Hast Du dann nur ein shop.php welches die Aktionen als "action" Parameter bekommt? Und dann im Script ein Switch der das geforderte Script läd? Was ist bei Dir mit globalen Daten, also definierten Konstanten, Parsen von Config-Daten, Einbinden der DB... Klasse, wie bindest Du das auf jeder Seite ein?
Siehe oben. Auch wenn es nicht so aussieht, GET- und POST-Parameter kommen mit obiger RewriteRule beim Skript an. URL-Parameter werden von mod_rewrite sowieso gesondert betrachtet.
Wieso machst Du so viel mit den Rewrite-Rules? Diese Logik habe ich in meinem main-php, bzw. das verwendet noch gar keine Parameter, die werden erst in dem von main.php geladenen Script ausgewertet.
Wirklich zentrale Konfigurationsdaten, die das Shop-Skript interessieren würden, habe ich nicht.
Nicht? Aber man hat doch immer so Dinge wie root-path, root-url, DB-Userdaten, Einstellungen für Template-Engine, Debug-Modus...
Die Datenbankklasse bindet ein kurzes Include ein, damit der DB-Zugang bekannt wird. Ansonsten war es das.
in jedem Script wo benötigt? ich weiß z.B. dass ich die DB eigentlich immer brauche, daher binde ich sowas gobal ein.
Im Zweifel würde ich für zentrale Konfigurationsdaten tatsächlich noch eine Extra-Klasse basteln.
Habe ich auch gemacht.
Spannend wäre in diesem Zusammenhang die Möglichkeit, solch eine Klasse als Bestandteil der Session abzuspeichern und wiederherstellen zu lassen. :)
Ja, aber was bringt das? Ich weiß jetzt keine genauen werte, aber das Parsen und Instanzieren aller "Basis-Klassen" in main.php dauert nur wenige 1/100 Sekunden, vermutlich durch Verwendung eines Opcode Caches, der dessen Opcode eh im Ram hält. Das ist sicher noch schneller als aus dem /tmp Files. Wobei ich gerade ein wenig damit experimentiere Session-Daten im Shared Memory zu halten (mm-Erweiterung), hat nur den Haken dass die Daten darin nicht gelockt werden, das heißt theoretisch können Inkonsistenzen entstehen, ob und wie sich das praktisch auswirkt habe ich aber noch nicht so ganz raus.
Inzwischen bin ich der Auffassung, dass man Funktionalitäten möglichst in Methoden packen sollte [...]
Bei meinem aktuellen Ansatz macht es nicht viel Sinn, aus den einzelnen Anweisungen, was im Falle des Aufrufs einzelner Seitentypen zu tun ist, eine Klasse mit API zu machen. shop.php ist in diesem Sinne eine "Klasse". An dieser Stelle werden die Nachbarschichten verknüpft - einmal in Richtung Präsentationsschicht (Templates), und dann in Richtung Session/DB. shop.php selbst ist die Businesslogik, die man in meinen Augen nicht trennen kann.
Naja, ich Trenne z.B. die Datenbank-Zugriffe von den einzelnen Objekte n durch DAO-Klassen speziell für jedes Objekt der Business-Logik. Templates rufe ich eigentlich "proredural" auf, das heißt das Script was in main.php eingebunden ist macht irgendwas mit den Methoden der Objekte, und gibt an Ende über das Template-Objekt die Daten aus. Das könnt eman sicher auch noch kapseln, aber da sehe ich noch keinen Vorteil drin, wenn gleich das auch viele Leute so machen.
Und die Aufteilung in unterschiedliche Dateien, die logisch ein Skript bilden (jedenfalls nicht in unterschiedliche Klassen zerfallen), hilft bei der Übersicht.
Das stimmt. Da die Aktionen normalerweise in einer eingebundenen Klasse stehen habe ich im Script eigentlich nur noch die Ablaufkontrolle stehen.
So nett so Dinge wie $template->assign($db->getAll('SELECT....')) auch sein mögen, gerade das darf man nicht machen. Ok, SQL-Befehle haben in der Businesslogik nichts verloren, aber Dinge wie $template->assign($db->getList($category)) macht man.
$db ist bei mir ja die Abstraktions-Klasse, die kann nur mit SQL umgehen. Die wird von den DAO-KLassen angesprochen. Aber bei mir würde das anders aussehen:
DB ist die Abstraktionsklasse(PEAR::DB...) Katalog ist die Katalog-Klasse KatalogDAO ist die Klasse die für den Zugriff auf die Datenbank für die Katalog-Klasse zuständig ist, diese verwendet DB
$katalog = KatalogDAO::loadData(); // loadData() holt Daten aus DB für $id und schreibt die per set...() Methoden in ein neues Katalog-Objekt und gibt eine Referenz zurück
$template->assign($katalog->getList()); // sowas würde ich dann machen, getList ist hier eine Methode von Katalog und gibt die Liste als Array aus
Richtig böse wäre, wenn sich die Template-Engine auf Anweisung der Businesslogik unter Umgehung der DAO mithilfe der DB-Klasse die Liste direkt aus der DB holen würde.
Ja, PHP erfordert Disziplin ;-)
Aber nur deshalb, weil das eine oder andere "alles erlaubt", habe ich ja noch keinen funktionierenden Code. :) Irgendwann muß ich immer den Schritt machen, mich von all der schönen Univeralität zu lösen und endlich konkret werden.
Klar, aber man sollte immer die Modularität im Hinterkopf haben - sofern das Projekt das erfordert. Die Objekte müssen möglichst gutgekapselt sein, damit Änderungen an einer Stelle möglichst wenige Auswirkungen auf andere Teile der Software haben, denn was das bedeutet habe ich am eigenen Leibe erfahren ;-) Am Anfang ist das kein Problem, aber irgendwann wird es immer schwieriger sich zu erinnern oder aus dem Code rauszulesen wo die gemachte Änderung denn jetzt überall Einfluss hat. Möglicherweise man vergisst eine Stelle und dann passiert Monate später ein komischer Fehler den sich keiner erklären kann...
Jetzt kann ich zu diesem Zweck natürlich erstmal einen riesigen Overhead basteln, in dem jede Datenbankabfrageergebniszeile als Objekt dargestellt wird, um dann mit viel drumherum und mehreren Schichten von Objekten endlich mal auszudrücken, dass die abgefragte Liste der Artikel endlich mal ausgegeben werden soll.
Ja, aber Du verwendest ja auch nicht sowas wie
$res = mysql_query('SELECT * FROM katalog'); foreach($row=mysql_fetch_array($res)) echo "<li>$row['prod_name'] ($row['prod_id'])</li>";
So würde man auch ne Menge Code vermeiden, und man hätte direkt seine Liste. Ist halt eine Frage wie weit man das treiben will. Ich denke je komplexer die Software und desto mehr Leute dran arbeiten desto mehr Konventionen, Kapselungen, Schnittstellen... braucht man.
Ich kann aber genauso gut auch die Abkürzung nehmen und für die Businesslogik eine Methode in der Session-Klasse nutzen, die mir ein Array aus der DB abfragt und intern einfach nur den dafür notwendigen Query an die Query-Methode aus der DB-Klasse leitet und das Ergebnisarray zurückliefert.
Wie gesagt - Disziplin ist das Zauberwort ;-)
Viele Grüße Andreas