Antworten: 44
|
|||
reVerB Geekboy Beiträge: 1237 |
# Thema - 03.07.2011 um 11:02 Uhr
Sooo... Jetzt hab ich da mal was für den einen oder anderen PHP-Pro, der schon Erfahrungen in OOP haben. Überzeugt bin ich zwar immer noch nicht von OOP. Aber es scheint ja immer mehr gefordert zu sein, OOP einzusetzen. Daher setze ich mich mal damit auseinander und schreibe ein eigenes kleines Script. So weit so gut. Aber nun stehe ich vor folgendem Problem: Ich habe eine Datenbankklasse geschrieben (ist noch nicht fertig ^^ Keine Panik). Aus gründen der Performance soll diese Permanent im Hintergrund als Objekt die Datenbankzugriffe ermöglichen. Nun stoße ich aber auf einmal auf einen kleinen Widerspruch. Denn ich habe mir ja Beispiele für DB-Klassen angesehen. Die einen nehmen ein Objekt dieser Klasse und reichen dieses in andere Klassen über global durch (Das ist auch mein Plan). Andere allerdings Vererben die DB-Klasse überall in die Klassen, die Datenbankzugriff haben wollen. Das bedeutet, das jedes mal eine neue Datenbank-Verbindung aufgebaut bzw. geschlossen wird. Bei zum Beispiel vielen Boxen im Template meiner Meinung nach bzgl. der Performance gegenüber suboptimal. Die nächsten nehmen eine Klasse als DB-Connector und eine Klasse mit den ganzen DB-Methoden und erben diese an alle Klassen weiter, die Zugriff auf die Datenbank brauchen. Die erste Variante scheint mir am einfachsten. Aber was ist zu empfehlen. Hier mal meine bisherige Klasse (Kam noch nicht zum testen. Daher könnten noch Fehler enthalten sein) Meine Klasse...
Nebenbei kann ich ja eine weitere Frage stellen. Warum soll ich Try und Catch einsetzen, wenn das Ding nicht annähernd auf alle Fehler reagiert. In anderen Sprachen kann Try Catch auf ALLE Fehler reagieren und dann einem beim Auswerten helfen. Hier muss ich bei PHP für nahezu alle Fehler eine eigene Überprüfung implementieren, das auf dauer echt tierisch nervig wird. Wie macht man das richtig? Ich hoffe es kann sich einer die Zeit nehmen, mir dabei kurz zu helfen. Denn meiner Meinung nach ist Try Catch im aktuellen Stadium irgendwie Blödsinn. Danke im vorraus. Zuletzt editiert von reVerB am 04.07.2011 um 10:06 Uhr (1x Editiert)
|
||
Inaktiv |
|
||
jokey Try to beat me Herkunft: Hamburg Beiträge: 184 |
# Antwort: 1 - 03.07.2011 um 11:19 Uhr
Auf welche Fehler kannst du denn nicht reagieren? Ich stimme dir zwar zu, dass PHP nicht alle auftretenden Probleme behandelt, viele sind aber Low-Level und bedeuten eher Code Probleme als echte Fehler... EDIT: Und für dein anderes Problem gibt es ein Konstrukt namens Singleton Zuletzt editiert von jokey am 03.07.2011 um 11:20 Uhr (1x Editiert) |
||
Inaktiv |
|||
reVerB Thread-Ersteller Geekboy Beiträge: 1237 |
# Antwort: 2 - 03.07.2011 um 11:57 Uhr
Singleton? Sehr schön. Ein Begriff, den ich in keines der Bücher hier zu Hause lesen konnte. Dafür schon einmal vielen Dank. Bevor ich das aber nun einbauen, würde ich gerne wissen, wenn ich nur eine Instanz der Datenbankklasse dann erstellen kann, wie es sich verhält, wenn die Datenbankklasse an eine andere Vererbt wird. Habe ich dann auf die Eigenschaften der Datenbankklasse Zugriff? Denn es erschließt sich mir nicht so ganz, wie das gehandhabt wird. Wenn zum Beispiel die Instanz News, die von der Datenbankklasse erbt in eine Eigenschaft einen Wert setzt, ist dieser auch in der Instanz von zum beispiel Artikel vorhanden, das ebenfalls von dieser statischen Klasse erbt? Zum Thema Try/Catch: Ich habe es in meiner DB-Klasse versucht, damit die Fehler beim Verbindungsaufbau abzufangen. Das hat mal gar nicht geklappt. Und wenn ich nun in jeder Methode, wo theoretisch ein Fehler auftreten könnte, eine Überprüfung mit Werfen einer Exception UND dann noch der Try/Catch-Block beim verwenden dieser prüfenden Methode/Funktion implementieren muss, dann habe ich am Ende doppelt so viel Code, als würde ich es direkt prüfen. Entweder habe ich da tierisch nen Knick in der Logik oder das Ding ist im aktuellen Stadium wirklich müll. UPDATE: Ich hab es jetzt getestet. Es geht natürlich nicht mit der Vererbung^^ PHP meckert wegen dem Konstruktor. Ich habe aber jetzt beschlossen, die Klasse einfach zu teilen. Eine Klasse als Singleton für die Verbindung der Datenbank und eine Klasse, die schön hin und her geerbt werden kann. Der Vorteil ist, das ich die statische Klasse immer einsetzen kann, da diese ja Global verfügbar ist. Mal sehen, wie es läuft. Zuletzt editiert von reVerB am 03.07.2011 um 12:28 Uhr (2x Editiert) |
||
Inaktiv |
|||
jokey Try to beat me Herkunft: Hamburg Beiträge: 184 |
# Antwort: 3 - 03.07.2011 um 13:17 Uhr
Dass das für die Datenbank so nicht klappt, ist ja auch logisch. Du benutzt funktionale Aufrufe und erwartest, dass PHP dir davon ne Exception wirft. Das funktioniert aber so nicht. Um das zu erreichen, musst du entweder die Funktionen mit if auswerten und dann selbst eine Exception werfen oder du benutzt eine klassenbasierte API wie PDO. Ferner glaube ich, dass du nicht wirklich verstanden hast, was "Vererbung" ist und wie das OOP Modell aufgebaut ist. Man vererbt ja nicht etwas durch die Gegend. Vererbung, sprich Ableitungen bildest du, wenn du Eigenschaften eines Datenmodells, sprich einer Klasse, in einer anderen verfeinern / überarbeiten willst. Ist ein längeres Thema und lässt sich nicht ganz in einem kleinen Post hier beschreiben |
||
Inaktiv |
|||
reVerB Thread-Ersteller Geekboy Beiträge: 1237 |
# Antwort: 4 - 03.07.2011 um 13:38 Uhr
Ferner glaube ich, dass du nicht wirklich verstanden hast, was "Vererbung" ist und wie das OOP Modell aufgebaut ist. Man vererbt ja nicht etwas durch die Gegend. Doch das habe ich verstanden. Mir ist nur im Verlauf aufgefallen, das ich wohl mein erster Post nicht vernünftig geschrieben habe, wodurch du diesen nicht verstanden haben könntest. Mein primäres Ziel war nicht, die mehrfachen Instanzen eines Objektes zu verhindern. Mein Ziel war es, beim vererben der Datenbank-Klasse nicht mehrere MySQL-Verbindungen aufzubauen und gleichzeitig die Verbindung/Referenz sauber weiterzureichen, ohne auf so Dinger wie global zurückgreifen zu müssen, was unter Umständen beim Programmieren von Erweiterungen zum Problem führen kann. Die Datenbank-Klasse will ich in so weit vererben, das ich diese immer individuell verarbeiten kann und nicht immer die Eigenschaften zurückschreiben muss, um sie bei der nächsten Aktion neu zuweisen zu können. Dadurch, das ich nun das Singleton für die Datenbankverbindung nutze und auf dessen Parameter von überall Zugriff habe, kann ich nun die Datenbankklasse vererben, ohne weitere Verbindungen zu öffnen und die Methoden sowie Eigenschaften dynamisch einsetzen, da ich immer wenn ich sie brauche eine neue Instanz davon habe. Das habe ich nun geschafft. Hier mal meine DB-Klassen (Werden noch in 2 getrennte Dateien gepackt) ...
Alles getestet und alles funktioniert. Und du hast natürlich auch recht mit den MySQL-Funktionen. Damit eine Exception ausgelöst werden muss, damit try/catch funktioniert, weiß ich ja. Habe aber verplant, das es sein kann, das natürlich das werfen von Exceptions nur Einsatz in Objekten findet. Klingt ja auch logisch. Dann mach ich das mal lieber von Hand ^^ Zuletzt editiert von reVerB am 03.07.2011 um 13:40 Uhr (1x Editiert) |
||
Inaktiv |
|||
SCHIRI Weltmeister Herkunft: Hamburg Beiträge: 5299 |
# Antwort: 5 - 03.07.2011 um 17:09 Uhr
Aber es scheint ja immer mehr gefordert zu sein, OOP einzusetzen. Eine OO-Umsetzung zu wählen bringt gewisse Vorteile, erhöht aber auch schnell sehr stark die Komplexität, wenn man diese wirklich ausreizen will. So wie du das ganze grade angehst, bekommst du so gut wie keinen der Vorteile. Dass eine News-Klasse von database erbt, macht absolute keinen Sinn. Es gibt gewisse Merkmale, an denen man erkennt, ob einen Vererbung Sinn macht. Eine davon ist "ein A ist ein B". z.B: ein Auto ist ein Fahrzeug -> Es könnte Sinn machen, wenn die Auto-Klasse von der Fahrzeug-Klasse erbt. ein Admin ist ein User -> Es könnte Sinn machen, wenn eine Admin-Klasse von einer User-Klasse erbt. Eine News ist aber keine Datenbank und daher macht es absolut keinen Sinn, eine News-Klasse von deiner database-Klasse erben zu lassen. Eine MySql-Datenbank ist eine Datenbank, also könnte, wenn überhaupt und wenn du das brauchst, eine MySql-Klasse von Database erben. Ausserdem solltest du darauf achten Singleton und überhaupt statische Methoden und Properties zu vermeiden, weil ansonsten schon mal ein Großteil der OOP-Vorteile verloren gehen können. Es macht Sinn darauf zu achten, dass Klassennamen _realistische_ Objekttyp-Namen sind, die sich zumindest mit etwas Fantasie in die Realität übertragen lassen. Dann wird einem selbst auch etwas besser klar, was man eigentlich braucht und will. databaseConnect ist ein Verb. Vereinfacht mal eben in "DatenbankVerbinden" übersetzen. new databaseConnect(); heisst also: "Gib mir ein neues DatenbankVerbinden". Was du aber meinst ist wohl eher "Gib mir einen Verbindung zu einer Datenbank". Also macht es Sinn, die Klasse DatabaseConnectIon zu nennen. Welchen konkreten Vorteil versprichst du dir selbst davon, die Funktionalität, die du bisher hier gezeigt hast ein zwei Klassen abzubilden? Bzw was genau können deine 120 Zeilen Code besser, als wenn du keine Klassen benutzt hättest? Wenn du das ganze richtig durchziehst, gewinnst du einen HAUFEN an Vorteilen, aber wenn du es falsch machst, machst du dir nur unnötig viel Arbeit. Dafür musst du dich aber erstmal selbst entscheiden, auf welche Vorteile du hinarbeiten willst. Ein Vorteile wäre z.B. sicherstellen zu können, dass eine "News" oder ein "User" bzw alle aus der Datenbank geladenen Daten nur höchstens einmal Pro Request vorkommen. Sprich wenn du an zwei Stellen (z.B. im Content und in einer Navlist) SELECT * FROM users WHERE id=23 sagst, dass der Query nur einmal ausgeführt wird und beim zweiten mal der schon in Php geladenen User direkt da ist. Ein andere Vorteile wäre es Anhand Objekten ein datenbankangostischen Query bauen zu können. Sodass du beim Schreiben des Queries nicht mehr auf die unterschiedliche Syntax zwischen Mysql, Sqlite, PostgrSql und sogar vllt ganz anderen Datenbank wie MongoDB achten musst. Noch ein Vorteile könnte sein, dass du gar keine Queries mehr schreiben musst, sondern nur noch get(), load(), set() und save() Methoden aufrufst. Je mehr du von diesen möglichen Vorteilen benötigst, desto Komplexer wird das ganze System und die menge des vorab zu schreibenden Codes steigt extrem. Also musst du ich erstmal entscheiden, ob du das brauchst, ob es lohnt und was davon du brauchst. Ich habe letztes Jahr wirklich so etwas gebaut, was Grundlegend die eben beispielhaft genannten Vorteile bietet. Der Zeitaufwand waren ein paar Monate und das hier sind einige der dabei entstandene und benötigten Klassen: AdapterBase - Das was bei dir databaseconnector ist MySqlAdapter - speziell für MySql SqliteAdapter - für Sqlite AdapterRegistry - verwaltet mehrere Datenbankverbindungen Query - Objekte dieser Klasse stellen einen Query da IdentityMap - Sorgt für die genannte Einzigartigkeit von Objekten aus der Datenbank DataSource - das was bei dir database ist Model - das wovon z.B. "News" erbt und repräsentiert eine Zeile in der DB Property - repräsentiert eine Spalte in der Datenbank StateBase - Stellt den Status von Änderungen an einem ungespeicherten Datenbank Objekt da (z.B: eine News, bei der der Title geändert, aber noch nicht pe UPDATE gespeichert wurde) CleanState DirtyState DeletedState IntermediateState Path - Wird zur Modellierung von INNER JOINs benutzt. Order - Wird zur Datenbankunabhängigen Modellierung von Queries benutzt (ORDER BY) Operation - Wird zur Datenbankunabhängigen Modellierung von Queries benutzt (AND OR NOT NULL) Condition - Wird zur Datenbankunabhängigen Modellierung von Queries benutzt (WHERE > = < >= <= != IN) Aggregator - Wird zur Datenbankunabhängigen Modellierung von Queries benutzt (GROUP BY SUM, MAX, MIN, AVG) Soweit so gut, das war nur einige der Klassen, die bei mir am Ende enstanden sind. Dafür habe ich aber auch viele Vorteile Gewonnen, die ich mir am Anfang als Ziel gesetzt habe. Allerdings auch wieder einige Nachteile wie z.B. dass man kein SQL mehr per Hand schreiben darf oder überhaupt einen gewissen Overhead in Bezug auf die performance. Ich persönlich würde zwar sagen, dass es sich gelohnt hat, aber nur des OOP-Willen wegen einen solchen Aufwand zu treiben stelle ich mal in Frage Wenn man andere Anforderungen hat oder diese nur anders gewichtet, könnte man das ganze System auch wieder anders aufbauen. Nur wenn du nicht weisst, was du willst, endest du entweder bei zwei bis drei riesigen Monsterklassen oder bei einer Unmenge an kleinen Klassen, weil du nicht weisst, warum etwas wirklich in eine Eigene Klasse muss und warum nicht. /e: Oh, ist jetzt etwas länger geworden, ich hoffe aber hilfreich. ------------------ www.laszlokorte.de |
||
Inaktiv |
|||
reVerB Thread-Ersteller Geekboy Beiträge: 1237 |
# Antwort: 6 - 03.07.2011 um 18:31 Uhr
@SCHIRI: Was von dir kommt ist eigentlich immer hilfreich Also mein Ziel ist es, ein Mainstream-System als Interface für Webanwendungen zu Desktop-/Mobileapplikation zu schreiben. Dieses soll außerdem zur Not das ganze auch alleinstehend nur zur Kommunikation mit der Datenbank dienen. Eine Weboberfläche kann dann als Adminwerkzeug genutzt werden. Dabei soll nur MySQL verwendet werden. Alles andere wäre nicht Mainstream. Die stärke soll in der Absicherung der Verbindung zwischen App und Interface liegen. Ich selber hatte vor, das ganze Prozedural zu schreiben. Denn wie du schon sagst kann der Overhead extrem werden. Das Programm soll aber jede Millisekunden für sich und nicht für den Overhead habe. Die ersten, die ich damit konfrontiert habe schrien allerdings nach OOP. Als ich sagte, das es Prozedural sein soll, war gleich das Interesse weg. Ich werd noch einmal eine Nacht drüber schlafen. Vielleicht komme ich dann zu einer Erkenntnis, wie ich das ganze anpacke. Das Prinzip von OOP habe ich soweit verstanden. Ich habe nur nach einer Möglichkeit gesucht, wie ich die Vorteile der Vererbung auch bei einer Datenbankverbindung nutzen kann. Aber es sieht so aus, das es besser ist, wenn man nur eine DB hat, mit Hilfsfunktionen wie bei CSP zu arbeiten. Und selbst damit sind mehere verschiedene Datenbanktypen möglich. |
||
Inaktiv |
|||
hajo VIP - Poster Herkunft: Barsbüttel Beiträge: 9411 |
# Antwort: 7 - 03.07.2011 um 18:32 Uhr
crud tools reichen oftmals aus, alles was darüber hinaus geht schreit meistens schon nach einem or-mapper und dann wird es erstens eklig und zweitens hat man da länger was von ^^ ------------------ ClanSphere - professional clan care starts here |
||
Offline |
|||
reVerB Thread-Ersteller Geekboy Beiträge: 1237 |
# Antwort: 8 - 03.07.2011 um 18:57 Uhr
CRUD? Netter Begriff... Definition bitte? Naja mal sehen, was ich daraus mache. Die Replies haben auf jeden Fall gereicht, das ich an meinen Plänen zweifle und diese umgestalten sollte. |
||
Inaktiv |
|||
SCHIRI Weltmeister Herkunft: Hamburg Beiträge: 5299 |
# Antwort: 9 - 03.07.2011 um 19:07 Uhr
/edit: CRUD = Create, Read, Update, Delete, also die 4 basis operationen zum Verwalten von Daten. Vererbung ist sehr oft eine schlechte Möglichkeit das Ziel zu erreichen auch wenn es eines der zentralen OOP-Features in Php ist. Es gibt andere Möglichkeiten, wie ein Proxy-Objekt, das Delegator-Pattern oder der Decorator-Pattern. Dabei werden Objekte ineinander geschachtelt und Methoden-Aufrufe weitergereich. als Beispiel hätte z.B. die News-Klasse eine instanz-Variable, in der ein Objekt der Klasse DatabaseConnection gespeichert ist. könntest das z.B. so machen: $db = new DatabaseConnection('user', 'password', 'localhost', 'database'); $news = new News($db); $news->save(); und erzeugst damit ein News-Objekt, was weiss, in welche Datenbank es gespeichert werden soll. Aber News erbt nicht mehr von DatabaseConnection. Oder andersrum: $db = new DatabaseConnection('user', 'password', 'localhost', 'database'); $news = new News(); $db->save($news); So würdest du die Logik, um ein News-Objekt in einen INSERT INTO oder UPDATE-Query umzuwandeln aus der News-Klase in die DatabaseConnection-Klasse verlagern, was den Vorteil hat, dass du die News-Klasse damit nicht voll müllst, weil eine News ja eigentlich gar nicht zu wissen hat wie genau sie gespeichert werden soll. Noch eine Möglichkeit wäre so etwas: $db = new DatabaseConnection('user', 'password', 'localhost', 'database'); $queryBuilder = new MySqlQueryBuilder(); $news = new News(); $query = $queryBuilder->buildInsertQueryFor($news) $db->execute($query); Um die DatabaseConnection nicht immer neu zu erzeugen, solltest du überhaupt eine Registry-Klasse bauen und nur diese als Singelton umsetzen und alle anderen Objekte, die du mehrfach brauchst (Datenbank-Verbindung, Mailer, Abcode-Klasse, Logger... nur um ein paar Beispiele zu nennen), dort rein packen. z.B. in dem du sowas in eine Config-Datei oder ähnliches packst: $registry = Registry::getInstance(); $registry->set('db', new DatabaseConnection(....)); $registry->set('logger', new Logger('path/to/log_file.txt')); // und was du sonst noch an global wichtigen objekten hast. und dann kannst du überall in der Anwendung Registry::getInstance()->get('db'); aufrufen um an die Datenbank Verbindung zu kommen. Selbst wenn du nur genau die 4-5 Funktionen aus Clansphere in eine DB-Klasse umbauen würdest, gewinnst du schon. Nämlich das Feature mehrere Datenbank-Verbindungen parallel auf haben zu können, dadurch, dass du auf globale Variablen verzichtest. Letztendlich macht es also schon Sinn, auf OOP zu setzen, auch wenn man keine riesig komplexen Features braucht. Man sollte nur trotzdem die Vorteile kennen und darauf hinarbeiten anstatt sie sich trotz OOP wieder zu verbauen, weil man nicht aufpasst. Übertreiben sollte man es aber auch nicht. Für dich vermute ich fast, dass es reicht einfach die PDO-Klasse, die bei Php schon dabei ist zu benutzen. Es hindert dich ja aber auch keiner auf ein fertiges ORM (Object Relation Mapper, also ein Framework, was sich darum kümmert, dir einen OOP-basierten Zugriff auf die Daten in einer relationalen(~sql) Datenbank zu geben, indem es dir Methoden zum suchen und speichern von Objekten anbietet. Das populärste davon in Php ist Doctrine. ------------------ www.laszlokorte.de Zuletzt editiert von SCHIRI am 03.07.2011 um 19:12 Uhr (2x Editiert) |
||
Inaktiv |
|||
reVerB Thread-Ersteller Geekboy Beiträge: 1237 |
# Antwort: 10 - 03.07.2011 um 20:08 Uhr
Naja. Ob die Daten nun als Array oder Objekt verarbeitet werden, kann ja irgendwie schnuppe sein. Aber das mit dem OOP wird langsam interessant. Wenn ich das richtig verstehe, ist es Ratsam, nicht von der databaseKlasse zu erben, sondern eher mit einem einmaligen Objekt dieser Klasse zu arbeiten. So hatte ich das ja am Anfang zuerst. Dann sollen die einzelnen Klassen wie News , Artikel etc. in den Methoden darauf zugreifen. Für meine Zwecke würde es dann ausreichen, für zum Beispiel User im Constructor abzufragen, ob ein Parameter gesetzt ist und ob dieser ein String oder ein Int ist. Wenn ein Int, soll er aus der Datenbank den User mit der ID=INT laden. Wenn ein String übergeben wird, so soll er den User mit dem NAMEN=STRING automatisch holen. Wird kein Parameter übergeben bzw. false, dann legt er ein neues Objekt ohne die Werte an. Dann kann man auf die Eigenschaften zugreifen und ihm die Werte für die Datenbank übergeben. Mit einer Methode wie z.B. $user->save() legt dann das Objekt die Daten in der Datenbank ab. In der Methode werden nun die Daten über die Methode einer Instanz der Datenbankklasse in die Datenbank geschrieben. Dann muss ja nur das Problem mit dem Schreiben von mehreren Datensätzen auf einmal gelöst werden. Dann macht der ganze Kram nämlich auch richtig Sinn. Vor allem kann man dann per Interfaces dafür sorgen, das alles auf die Datenbank abgebildeten Datensätze als Klassen die nötigen Methoden besitzen. |
||
Inaktiv |
|||
SCHIRI Weltmeister Herkunft: Hamburg Beiträge: 5299 |
# Antwort: 11 - 03.07.2011 um 20:47 Uhr
Array <-> Objekt ist insofern ein großer Unterschied, als dass ein Objekt einen eigenen internen Variablen Scope hat (protected properties) und über die Klasse definierte Methoden hat, die sich von Klasse zu Klasse unterscheiden, aber gleich heissen können. z.B. $user->delete(); # hier kann spezielle Logik eingebaut sein, die passieren soll, wenn ein User gelöscht wird (z.B. alle seine Comments anonymisieren) $news->delete(); # Bei der Löschung einer News kann etwas ganz anderes passieren (z.B. rss-feed-cache leeren) delete($news_as_array); # Wenn du alle Daten nur als Arrays vorliegen hast, kannst du nur eine delete-Methode haben, die dan RIIIIESIG ist, um alle die speziellen Fälle von "was passiert wenn das und das gelöscht wird" abzuhandeln delete_user($user_as_array) # Oder du machst für die verschiedenen Daten verschiedene delete-Methoden/Funktionen. Dann hast du aber das Problem, dass du einen Haufen an Funktionsnamen hast. Kannst dir ja auch gerne mal angucken, was ich letztes Jahr gebastelt hatte: https://github.com/laszlokorte/Dataphant/blob/master/demo.php Wenn ich das richtig verstehe, ist es Ratsam, nicht von der databaseKlasse zu erben, sondern eher mit einem einmaligen Objekt dieser Klasse zu arbeiten JaDann muss ja nur das Problem mit dem Schreiben von mehreren Datensätzen auf einmal gelöst werden. Dann macht der ganze Kram nämlich auch richtig Sinn. Vor allem kann man dann per Interfaces dafür sorgen, das alles auf die Datenbank abgebildeten Datensätze als Klassen die nötigen Methoden besitzen. Da macht es z.B. Sinn eine Klasse RecordList oder so zu haben. Dann kannst du sowas machen: $list = new RecordList(); $list->add($user1); $list->add($user2); $list->save(); bzw andersrum kannst du dann deine Database-Klasse so bauen, dass du als Ergebnis eines Select-Queries kein Array bekommst, sondern das direkt in eine RecordList umgewandelt wird. Das ganze wird jetzt aber schon sehr schnell sehr komplex. Meiner Meinung nach kann man sich da eigentlich nur zwischen zwei Sachen entscheiden: - einen simplen Wrapper um PDO bauen, der einem das schreiben von SQL-Queries einen Tick vereinfacht - ein fullfeatured ORM benutzen oder selber bauen. Alles dazwischen ist schwierig weil man wie schon gesagt immer von Hütchen zum Stöckchen kommt, wenn man sich denkt: ... jetzt muss ich nur noch mehrere Records auf einmal speichern können ... jetzt muss sich nur noch mehrere Records auf einmal laden können ... jetzt will ich dass ich direkt alle Comments eines Users laden kann, ohne den Query dafür noch schreiben zu müssen, wenn ich den User doch schon habe. Eigentlich könnte der User das laden der Comments doch direkt selbst machen ... Wenn ich jetzt einen User habe und einen Comment erstelle und dem User hinzufüge, und dann den user speicher, sollte der Comment doch auch gespeichert werden... Und so weiter... und wenn man erstmal anfängt das zu bauen, scheint die nächste Schritt der vereinfachung immer logischer zu sein und dann wäre dies doch auch noch ganz schön und jenes auch noch... wie wäre es wenn der User direkt eine getReplies() Methode hat, mit der man alle Replies auf die Comments, die der User geschrieben hat, bekommt? Das ganze Zeug geht ohne OOP jedenfalls grundsätzlich einfach nicht. Aber wenn man erstmal anfängt ist es schwer die Grenze zu ziehen, was man braucht und was nicht. z.B. deine Idee mit dem Kontruktor: Was passiert, wenn man folgendes macht: $user1 = new User(4); $user2 = new User(4); Das ruft den normalen Contructor der User Klasse auf und erzeugt dir zwei User-Objekte, das kannst du nicht verhindern. Aber eigentlich wolltest du nur an zwei stellen im Code (wie gesagt, navlist und content z.B.) den User mit ID=4 haben. Solange sich die User nicht ändern ist auch nicht mal so das Problem, dann hast du den User eben in zwei Objekten repräsentiert. Wenn du aber sowas machst: $user1 = new User(4); $user1->set('nickname', 'SCHIRI'); $user2 = new User(4); Dann wird das Problem schon größer, denn dann wird in der Navlist und im Content für einen user zwei verschiedene Nicknames angezeigt. Also musst du dich entweder darum kümmern, dass die Objekte zur Laufzeit synchron gehalten werden (ist aber (so gut wie?) gar nicht möglich) oder du musst beim zweiten Konstruktoraufruf verhindern, dass ein zweites Objekt des gleichen Users erzeugt wird. Das geht aber nicht, wenn du den normalen Konstruktor von Php direkt benutzt. Also musst du einen Wrapper als eigene statische Methode schreiben und dann so aufrufen: User::new(4); User::new(4); beim ersten Aufruf wird sich dann gemerkt, dass schon ein user mit id4 geladen wird und der wird dann wieder in eine Registry gespeichert und beim zweiten Aufruf von new(4) zurück geholt ohne noch mal einen Query zu senden. Ist aber new() auch der richtige Name für die Methode? sollte es nicht lieber search() oder find() oder select() heissen? Was ist, wenn du einen User anhand seines Alters finden willst? Irgendwann braucht man das bestimmt mal, oder? Nur um mal so ein paar Beispiele zu nennen für Probleme, die evtl, aber irgendwann sicher, auftreten, wenn man so ein ORM-ähnliches System bauen will. Mein Fazit wäre also nach wie vor, es entweder dead-simple zu halten oder sich damit abzufinden, dass einiges, was eigentlich Sinn macht, einfach nicht geht oder sich eben ein fertiges komplexes System zu schnappen oder soetwas selbst zu bauen, dann aber mit einem Haufen Arbeit zu rechnen ------------------ www.laszlokorte.de |
||
Inaktiv |
|||
reVerB Thread-Ersteller Geekboy Beiträge: 1237 |
# Antwort: 12 - 03.07.2011 um 21:27 Uhr
Noch ist das Interesse nicht verloren gegangen ... Ok !! Bis hierhin bin ich super mitgekommen. Eine Kleinigkeit habe ich da noch. Wenn ich jetzt eine Liste mit sage ich mal 10 Usern aus der Datenbank laden will. So kann ich doch eigentlich diese nicht aus einem Objekt des Users machen. Wenn ich das jetzt über zum Beispiel der Recordlist mache, kann ich ja dann theoretisch $list->load(Parameter für Sortierung etc.). Da ich nun aber bezweifle, das die RecordList für jede Klasse neu angelegt werden soll ala $list = new userRecordList() kann ich ja dem Load THEORETISCH einen weiteren Parameter übergeben, der zum Beispiel users oder news etc enthält, um so das gewünschte Objekt zum Laden zu erhalten. Oder noch besser übergebe ich das gewünschte Objekt gleich als String, um so mit instanceof die gesamte Instanz des RecordList-Objektes auf eine Bestimmte Art von Objekt zu setzen. Zum Beispiel so: $userList = new recordList('users'); $userList->add($user1); // geht $userList->add($news1); // geht nicht, da auf instanceof geprüft wird $userList->add($user2); // geht wieder $userList->load('DESC',0,10); // wird durch das 'users' beim instanzieren der Liste an die richtige Datenbank gesendet (Natürlich müssten die Adds jetzt weggedacht werden) Ich hoffe, das ich das jetzt so richtig gerallt habe. |
||
Inaktiv |
|||
SCHIRI Weltmeister Herkunft: Hamburg Beiträge: 5299 |
# Antwort: 13 - 03.07.2011 um 22:49 Uhr
Noch ist das Interesse nicht verloren gegangen ... War auch gar nicht meine Absicht Ja ich würde sagen du hast es soweit richtig verstanden. Ob man für die verschiedenen Daten auch verschiedene Listen-Klassen macht, ist im Prinzip Geschmackssache. Ich selber haber lieber etwas weniger Klassen um die ich mich Kümmern muss. Viele Leute stehen aber voll drauf immer einen Grund zu finden etwas in eine extra Klasse zu trennen. ------------------ www.laszlokorte.de Zuletzt editiert von SCHIRI am 03.07.2011 um 22:49 Uhr (1x Editiert) |
||
Inaktiv |
|||
hajo VIP - Poster Herkunft: Barsbüttel Beiträge: 9411 |
# Antwort: 14 - 03.07.2011 um 22:52 Uhr
separation of concerns *hust* ------------------ ClanSphere - professional clan care starts here |
||
Offline |
|||
SCHIRI Weltmeister Herkunft: Hamburg Beiträge: 5299 |
# Antwort: 15 - 03.07.2011 um 22:59 Uhr
Aber was ist der Unterschied zwischen einer UserRecordList und einer NewsRecordList? Beide beinhalten eine List mit Objekten, lassen sich iterieren, zählen, speichern, löschen... Die Frage istob sich die Klassen wirklich unterscheiden würden und wenn sie das nicht tun, brauchst du auch keine extra Klasse. ------------------ www.laszlokorte.de |
||
Inaktiv |
|||
reVerB Thread-Ersteller Geekboy Beiträge: 1237 |
# Antwort: 16 - 04.07.2011 um 00:13 Uhr
Irgendwo müssen sie sich ja unterscheiden. Ansonsten würden beide das gleiche Ergebnis haben. Aber du hast natürlich recht. Aus diesem grund finde ich die Idee $list = new RecordList('users'); besser als $list = new UserRecordList();. Denn so benötige ich nur eine Klasse und ich kann sie dann nur durch den Parameter seinen Aufgaben zuweisen. Sooo mein Collegeblock füllt sich langsam aber sicher immer mehr ^^. 16 DIN A4 Seiten hab ich schon voll Eine Sache noch: Wie bekomme ich das denn jetzt nun hin, ähnlich wie bei func:show (Den Code dahinter) Klassen nach App dynamisch zu laden. Denn ein Interface, das nur aus dem Core besteht bringt nicht viel. Meine Erste Idee war es, einen Loader für jede App (bei CSP Modul) zu Basteln, der mir die richtige Datei lädt. Das ist ja schon einmal gut und schön. Nur wie lade ich bei zum Beispiel index.php?app=news die Klasse news aus dem entsprechenden Mod/der App dynamisch. Ich habe gelesen, das man das direkt über eine Variable machen kann. In etwa so: $app = $_GET['app']; $news = new $app(); Kann man das so machen oder ist das unsauber? Denn eine Alternative finde ich nicht. Allerdings wirkt es ein wenig komisch, wenn man das so macht. Wenn läuft das ganze ehh über einen Loader. Direkt einbinden tue ich das so nicht. Denn über den Loader kann ich zum Beispiel ja dann alle GET und POST-Parameter verarbeiten und in eine Parameter-Liste packen. Dabei kann ich dann gleich GET-Parameter prüfen und absichern. Dazu würde sich ja eine Parameter-Klasse eignen, die Methoden zum prüfen von einfachen Bedingungen beinhaltet. Von dieser erben dann die Klassen für POST und GET, wobei diese wiederum spezifische Methoden enthalten. |
||
Inaktiv |
|||
SCHIRI Weltmeister Herkunft: Hamburg Beiträge: 5299 |
# Antwort: 17 - 04.07.2011 um 01:52 Uhr
Ach es geht dir auch noch um die Requestverarbeitende Logik... Da gibt es auch wieder einen Haufen Möglichkeiten der Umsetzung und man kann sich wieder beliebig austoben. Grundsätzlich ist es wohl Sinnvoll da erstmal den Begriff MVC zu nennen: Model/View/Controller Das ist ein DesignPattern nach dem eine Anwendung in 3 Zuständigkeitsschichten einteilen lässt: Model: Die Business und Daten Logik. Das ist die Überschrift zu dem bisher in diesem Thread diskutierten. eine News-Klasse oder User-Klasse ist eine Model-Klasse, weil sie ein in der Realität existierendes für die Anwendung zentrales Teil der Inhaltlichen Logik modelliert. Alle Methoden in der User-Klasse zusammen beschreiben, was ein User ist und was ein User kann, was mit ihm gemacht werden kann und wodurch er definiert ist. View: Die Darstellungslogik. Im Falle von Webanwendungen sind das die Templates. Controller: Die Logik, die den Ablauf der Anwendung kontrolliert und die Verbindung zwischen Model und View herstellst. Das ist der Teil auf den du jetzt hinaus willst. Controller kümmern sich darum, die erforderlichen Datenmodelle zu laden und das richtige Template auszuwählen und die Daten daran zu übergeben und am Ende die Antwort an den Client zu geben. In den typischen MVC-Web-Frameworks ist das ganze so umgesetzt, dass du Pro URL-Gruppe (?mod=news) eine Controller-Klasse hast (NewsController) die dann pro Sub-URL (&action=recent, &action=list, &action=new, &action=edit....) eine Methode definiert (in diesem Zusammenhang wird eine Methode dann auch "Action"). class NewsController { public function listAction() { ... } public function editAction() { ... } } Innerhalb dieser Action wird dann typischerweise das News-Model aufgerufen, sprich die News-Klasse benutzt. In deinem fall sähe die listAction also so aus: public function listAction() { $newsList = new RecordList('news'); render_template('news/recent.tpl', $newsList); } oder so ähnlich. Es gibt also nicht nur eine News-Klasse, sondern auch noch eine NewsController-Klasse. Die erstere kümmert sich wirklich um die Funktionalität einer einzelnen News und die letztere Kümmert sich um die Interaktion mit dem Benutzer via Browser. Für den Schritt um von einer url wie index.php?mod=news&action=list zu dem aufruf der passenden Methode zu kommen gibt es wieder beliebig komplexe Möglichkeiten. Im Prinzip ist an soetwas: $name = $_GET['mod']; $action = $_GET['action']; $controller = new $name(); $name->$action(); nichts falsch. Es ist nur nicht so komplex wie man es machen könnte und bietet entsprechend nicht so viele Konfigurations und Erweiterungsmöglichkeiten. In den gängigen Frameworks ist es inzwischen Standard eine Router-Klasse zu haben, in der man per Konfigurations-Datei URL-Pattern (Modrewrite-ähnlich) Konfigurieren kann. z.B.: $url_config['edit_news'] = array('pattern' => '/news/:id/edit', 'controller' => 'NewsController', 'action' => 'editAction'); Und das ganze geht noch weitaus komplexer. Das Beispiel hier ist quasi schon trivial. So ein Router lässt sich dann auch andersrum benutzen, um die Konfigurierten URLs wieder generieren zu können. generateURLFor('edit_news', 7); Erzeugt dann wieder /news/7/edit Je nachdem wie du das ganze in verschiedene Klassen verpackst, kannst du es natürlich auch wieder ermöglichen eine StandardRouter Klasse zu haben und eine DatabaseRouter-Klasse, die dir die URL-Konfiguration aus der DB läd. Blablabla und so weiter und so fort. Wie du siehst, in soetwas kann man viel Arbeit stecken Im Hintergrund entstehen viele Klassen, ziel ist es aber (zumindest Grob) am Ende pro Plugin eine Controller-Klasse, eine Model-Klasse, einige Templates und bei bedarf eine Routing-Konfiguration zu haben. Zumindest nach dem von mir grade beschrieben Ansatz... Es gibt neben dem MVC Ansatz aber auch noch andere Ansätze, die teilweise ganz anders funktionieren. ------------------ www.laszlokorte.de |
||
Inaktiv |
|||
reVerB Thread-Ersteller Geekboy Beiträge: 1237 |
# Antwort: 18 - 04.07.2011 um 10:05 Uhr
Auf jeden Fall einmal vielen Dank. Also zur Zeit sieht mein Konzept jetzt mal so aus. Als Listenüberschrift nutze ich die Hauptklasse und als Listenpunkte die Klassen, die erben: class Database
class security
static class registry Singleton class dataContainer - class fileContainer (Diese Listen können Daten aufnehmen und in Dateien speichern) - class databaseContainer (Diese Listen können Daten aufnehmen und in die Datenbank schreiben) - class tempContainer (Diese Listen können Daten aufnehmen und nur temporär verwalten)
class errorHandler
class appController
Das ist so mein Anfang gerade. Ich halte, da ich jetzt die Feinheiten richtig verstanden zu haben scheine, diesen Aufbau für Logisch ^^ Eine Kleinigkeit will ich aber noch wissen, da ich auch dazu nichts in den Büchern finde. Wie kann ich Klassen davor schützen, instanziert zu werden und dabei trotzdem noch die Möglichkeit haben, die Klasse an andere zu vererben. Kleines Beispiel von Oben: Es darf nur die Klasse MySQL instanziert werden. Bei der Klasse database darf das nicht funktionieren. Also: $db = new MySQL(); // darf gehen $db = new database() // darf nicht gehen *EDIT* Hab mal den Thread-Titel ein wenig angepasst. Zuletzt editiert von reVerB am 04.07.2011 um 10:20 Uhr (5x Editiert) |
||
Inaktiv |
|||
Mindcrime Geekboy Beiträge: 1155 |
# Antwort: 19 - 04.07.2011 um 11:57 Uhr
Kein Singleton benutzen wenn es eine algemeine datenbank klasse ist. Manchmal braucht man verschiedene datenbank connections (daten von die eine datenbank holen und in eine andere andere daten schreiben zb)... |
||
Inaktiv |
|||
SCHIRI Weltmeister Herkunft: Hamburg Beiträge: 5299 |
# Antwort: 20 - 04.07.2011 um 15:17 Uhr
ine Kleinigkeit will ich aber noch wissen, da ich auch dazu nichts in den Büchern finde. Wie kann ich Klassen davor schützen, instanziert zu werden und dabei trotzdem noch die Möglichkeit haben, die Klasse an andere zu vererben. Dafür musst du die Klasse als Abstract definieren. also: abstract class Database { } --------- Ansonsten: Du kannst dir die Klassen natürlich so auflisten um eine schöne Übersicht zu haben. Meiner Erfahrung nach funktioniert das aber nicht - besonders dann nicht, wenn man noch keine Erfahrung auf dem Gebiet der OOP-Entwicklung hat. Deiner Klassenstruktur mit den den Klassen die alle von Security erben machen in meinen Augen absolut keinen Sinn und diese Idee zeigt nur, dass da noch irgendwo ein Verständnisproblem vorliegen könnte. Warum soll Crypt von Security erben? Welcher Code soll in der Security-Klasse stehen, der in Crypt wiederverwendet werden muss? Welche Methoden hat die Crypt-Klasse mit der Client-Klasse gemeinsam? Vererbung setzt man hauptsächlich dann ein, wenn es für ein Problem verschiedene Lösungsumsätze gibt. (ganz grob) z.B. könntest du eine "Escaper"-Klasse machen und eine HTMLEscaper und JavascriptEscaper, die beide von Escaper erben. Die Escaper-Klasse gibt dann vor, welche welche Methoden definiert werden müssen und bietet evtl schon etwas Standard-Logik hat, die dann entweder direkt genutzt werden kann oder von den anderen beiden Escapern wiederverwendet werden kann. Im Prinzip wird es ab hier auch schwer das weiter so detailiert zu erklären, denn: - Ein gewisser Teil ist einfach geschmackssache und hängt stark davon ab, was genau man braucht. - Es gibt schon einen Haufen DesignPattern, die allgemein definiert worden sind, die man für vieles benutzen kann, die ich aber hier nicht erklären werde, weil das zu lange dauert Sich Klassennamen und Vererbungshierarchien auszudenken ist genau so, wie sich einfach Funktions und Datei namen aus zu denken. An einigen offensichtlichen Stellen kann es funktionieren, aber besonders ohne jemals ein paar zusammenhängende Klassen geschrieben zu haben, ist es quasi unmöglich so weit in die Zukunft zu denken, dass man schon am Anfang festlegen kann, dass man eine ObjectList brauchen wird, die von einem DatabaseContainer erbt. Es geht in der objektorientierten Entwicklung nicht um die Klassen sondern um die Objekte. Schau dir am besten mal ein paar Frameworks oder so an, um ein Bild davon zu bekommen, wie etwas aufgebaut ist. z.B. das Codeigniter-Framework oder Doctrine als ORM oder das von mir ein paar Posts weiter oben verlinkte ORM oder Symfony2 oder CakePhp... Das sind alles ziemlich verschiedene Ansätze soetwas anzugehen. ------------------ www.laszlokorte.de |
||
Inaktiv |
|||
Antworten: 44
|
Sie müssen sich registrieren, um zu antworten. |