Objektorientiert: Ausgabe von Listen
lima-city → Forum → Programmiersprachen → PHP, MySQL & .htaccess
abfrage
array
auto
beispiel
code
datenbank
datum
farbe
funktion
http
karre
kurs
liste
objekt
persistenz
setter
sinn
stelle
typ
zeile
-
Hi zusammen
Ich habe wiedereinmal eine Frage, in der ich nicht weiter komme und ich finde auch nichts dazu bei google (oder mir fehlen die richtigen Suchbegriffe).
Es geht um folgendes Beispiel:
Ich habe eine Klasse Auto.
Eigenschaften: Id, Farbe, Typ, Anzahl Türen
Gleichzeitig habe ich eine Datenbanktabelle mit dem Namen Auto, darin die Felder id, Farbe, Typ, Anzahl Türen.
Ich baue jeweils meine Klassen so auf, dass ich, wenn im Konstruktor eine Id mitgebe, dieser den zugehörigen Datensatz aus der Datebank selektiert und die Eigenschaften dem Objekt zuweist. Mit der Funktion "speichern" wird dann jeweils der Datensatz in der Datenbank aktualisiert. Ohne Id wird durch aufrufen der Funktion "Speichern" ein neuer Datensatz erstellt.
So, nun zu meinem konkreten Problem:
Ich möchte nun eine LIste aller meiner Autos erstellen.
Ist es nun sinnvoll, wenn ich via Select einfach die gewünschten Autos hole und anschliessend in der Liste die jeweiligen Objekte lade:
Also
SELECT id FROM auto WHERE farbe = "rot"
while($zeile = $result->fetch(PDO::FETCH_ASSOC) { $auto[$i] = new Auto($zeile['id']); echo '<tr><td>'.$auto[$i]->gibFarbeAus().'</td> <td>'.$auto[$i]->gibTuerenAus().'</td> <td>'.$auto[$i]->gibTypAus().'</td> </tr>'; $i++; }
Oder ist es besser, direkt den Selekt in eine Liste zu verwandeln:
SELECT id, farbe, anzahl_tueren,typ FROM auto WHERE farbe = "rot"
while($zeile = $result->fetch(PDO::FETCH_ASSOC) { echo '<tr><td>'.$zeile['farbe'].'</td> <td>'.$zeile['anzahl_tueren'].'</td> <td>'.$zeile['typ'].'</td> </tr>'; $i++; }
Erstere Möglichkeit ist ja von der Wartbarkeit her sicherlich viel besser und geschickter, dafür habe ich mehrere Datenbankabfragen (bei 6 Autos wären das dann mindestens 7 Abfragen [1x Ids, 6x Auto]).
Letztere Möglichkeit bedeutet dafür, dass das Objekt in sich nicht konstant ist, was ja eigentlich schlecht ist (z.B. wenn ich nun plötzlich die Käufer dieses Autos ausgeben möchte, müsste ich hier einen separaten Selekt einbauen, mit der ersten Möglichkeit könnte ich im Objekt eine Funktion bauen "gibKaeuferAus()" die ich dann auch an anderer Stelle nutzen könnte.
Also meine Frage ist nun:
Ist der Performance-Verlust durch die erste Möglichkeit allenfalls gar nicht so extrem wie ich das befürchte oder bin ich mit meiner Hobby-Objektorientiertheit total auf dem Holzweg ...
Merci für eure Hilfe.
Beste Grüsse
Meron
Edit: Möglichkeit 2 korrigiert.
Beitrag zuletzt geändert: 1.4.2014 18:13:27 von meron -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
das was du machst ist äußerst performancelastig.
also erstens:
du machst eine Abfrage, bei der eine Liste autos reinkommt -> genau eine Abfrage! Dann bekommst du alle Daten als ein Objekt rein, welcher Art auch immer, je nachdem, wie du drauf zugegriffen hast. PDO finde ich jetzt weniger Elegent im Umgang mit PHP, aber
2. du ziehst dir ein Array des Typs auto auf, ohne es zu befüllen. Wenn du es nämlich über den Konstruktor initial befüllst, sparst du dir vielleicht ein paar Zeilen Code, aber die Daten müssen da sein, während du befüllst
3. Wenn du objektorientiert arbeiten willst, macht es Sinn, die Sichtbarkeiten entsprechend anzupassen. Attribute sollte man nur über getter und setter setzen, dann kannst du zugleich Validitätsprüfungen durchführen, während das Objekt befüllt wird und hast damit das Risiko minimiert, dass deine Validitätsprüfung beim insert/update scheitert enorm minimiert.
Du könntest dem Konstruktor auch gleich das ganze teilarray zukommen lassen, woraus er sich dann sein Auto bastelt. Da PHP leider noch kein richtiges OOP unterstützt, sondern so eine Pseudo-Variante, kannst du noch keine Methoden überladen, d.h. du müsstest über abfragen im Konstruktor prüfen, was du da bekommen hast.
Ansonsten gut, dass du den OOP-Ansatz verfolgst, weiter so!
-
Danke für deine Antwort. Aufgrund dessen habe ich bemerkt, dass ich einen Fehler in meinem Beispiel habe, die zweite Möglichkeit sollte eigentlich wie folgt aussehen:
while($zeile = $result->fetch(PDO::FETCH_ASSOC) { echo '<tr><td>'.$zeile['farbe'].'</td> <td>'.$zeile['anzahl_tueren'].'</td> <td>'.$zeile['typ'].'</td> </tr>'; $i++; }
Also keine Erstellung des Objektes, aber wenn ich dich richtig verstehe sollte es ungefähr so aussehen:
Selekt2, welcher schon die Attribute ermittelt.
Dann die Werte des Objektes nicht im Konstruktor von der DB laden sondern via Setter vom SQL-Result übernehmen? Und anschliessend Ausgabe via getter (Ausgabe wie in Möglichkeit 1).
PDO verwende ich, da es im Tutorial welches ich gemacht habe als guter Nachfolger von mysqli beschrieben wurde, da dachte ich das wäre schon gut?
nochmals danke für deine Hilfe. -
hm, PDO gibts glaub schon länger als mysqli, also kein Nachfolger, es ist einfach eine andere Technologie, die Parallel entwickelt wurde.
Ansonsten mit gettern und settern passt es, aber instanzen musst du trotzdem aufziehen, wie:
$autos = new array(); foreach($result as $karre) { $autos[$karre['id']] = new Auto(); $autos[$karre['id']]->setAttributes($karre); }
mit einem Objektorientierten Ansatz schaut man ja, dass der Code unabhängig von den Technologien ist, also würdest du für den ganz sauberen Weg dir eine schnittstelle bauen, die dir dein $result liefert und dann das was in das result liefert kann egal sein, was es ist, ob PDO, mysql/+i oder ein xml-Dokument... der Kern bleibt gleich. gut, wenn du den Konstruktor mit übergabeparametern befüllst, kannst du den teil in der foreach als eine Zeile ausdrücken
$autos[$karre['id']] = new Auto($karre);
und du hättest dann gleich ein Assoziatives array(ok, es ist pseudo-Assoziativ, weil du immer noch über den Index rankommst). Also Objekte/INstanzen einer KLasse sollte man schon erzeugen, solange es keine statische Klasse ist. man kann in einer KLasse auch statische Funktionen hinterlegen, aber ich weiß jetzt grad nciht, ob PHP sowas schon zulässt...
und Tutorials sind zwar gut und nett, aber sie zeigen immer nur einen Weg auf und dann einen recht einfachen für ein fiktives beispiel. Wenn du ein Projekt hast, was oft frequentiert ist, wäre es aufgrund seines speicherhungers vielleicht nciht die optimale lösung... bei kleinerne Datenbeständen wäre auch eine XML-Lösung als Optimal in Betracht zu ziehen... imemr fallabhängig -
Ich würd den Konstruktur umschreiben, sodass man alle Instanzvariablen übergeben kann und all den Datenbankkrams rausschmeißen.
Anschließend schreibst du ein Repository-Klasse, die aus der Datenbank liest und dann die Objekte erzeugt
bzw. die Objekte ausliest und sie dann in die DB schreibt.
-
Danke für eure Antworten. Was mir nun noch unklar ist, ist folgendes:
ich habe es bisher so angedacht, jeweils in den settern Funktionen die Eingaben direkt zu prüfen (also um beim Beispiel zu bleiben: wenn eine Farbe eingetragen wurde direkt in der DB zu schauen ob diese Farbe gültig ist und vom aktuellen Benutzer auch verwendet werden kann. Natürlich war ich so faul und habe im Konstruktor die Werte aus der DB jeweils direkt auf die Attribute geschrieben, sodass setter Methoden nur bei einer effektiven Änderung zum Einsatz kamen. Ich nehme grundsätzlich an, dass die Daten aus der DB korrekt sind und nicht weiter geprüft werden müssen (vielleicht ist das auch schon mein erster Fehler). Eine Prüfung derer käme wiederum einem Performanceverlust gleich ... Deshalb wird mit den im Konstruktor übergebenen Daten das Objekt ungeprüft gefüllt? Oder soll ich mit einem Flag arbeiten, mit welchem ich die Prüfungen ausschalten kann?
Zudem: Was ist eine repository Klasse? Habe kurz bei google gesucht, doch ich fand mehr so Tools wie tortoise etc :-)
ich möchte eigentlich schon lange einen PHP Kurs besuchen um solche Dinge richtig zu lernen, doch mich fürchtets einen Kurs zu erwischen der mich total über- oder unterfordert ... Habt ihr mir da schlaue Tipps? -
Erstmal, komme ich nicht aus der PHP-Ecke und hab nur vor meinem Studium mit PHP gespielt.
Ich würd an deiner Stelle mir nicht 100% vertrauen :P Kann sein, dass man in PHP es anders handhabt.
Generell würde ich bei Methoden die Variablen checken:
http://us3.php.net/manual/en/function.assert.php
Nennt sich auch Precondition-Check: http://en.wikipedia.org/wiki/Precondition
Sowas wird dann aber meist während des Betriebs aus Performance-Gründen ausgeschaltet.
Also richtige User-Eingaben muss man wohl anders checken/behandeln (hab bisher aber noch nicht geguckt wie :x).
Glaub ich würde einfach eine Klasse Farbe erstellen, die im Konstruktor einen String erwartet und da kannst du ja prüfen, ob es eine gültige Farbe ist.
Dann einfach beim Auto-Setter prüfen ob es ein Farbe-Objekt ist.
Ich nehme auch immer einfach an, dass was in der DB ist, korrekt ist.
Sollte es ja eigentlich auch, wenn du mit Precondition gecheckt hast, aber ob
das andere auch so sehen weiß ich leider auch nicht
Aber sonst gibt es noch Postcondition für die Entwicklung:
http://en.wikipedia.org/wiki/Postcondition
Ja, Repository gibt s auch bei Git etc.
Was ich aber meinte war das Repository Pattern.
Hab mal versucht was zu finden, denke das ist was ich meine, auch wenn ich es nur überflogen habe.
Nun ist es Java und ich weiß nicht ob das Pattern auch gängig in PHP ist:
http://www.norberteder.com/das-repository-pattern-anhand-eines-beispiels-inkl-tests/
Im Prinzip ist es einfach eine Klasse die deine Auto-Objekte speichert, lädt, updated, löscht, etc.
Damit nimmst du technischen Code (Datenbank) aus deinem fachlichen Code (Auto).
Hat jetzt erstmal so keinen direkten Vorteil, aber bei größeren Projekten hilft es beim Verstehen
des Codes ungemein.
Dazu noch:
http://www.clean-code-developer.de/Separation-of-Concerns-SoC.ashx
http://de.wikipedia.org/wiki/Koh%C3%A4sion_(Informatik)
Zum Kurs kann ich leider nichts sagen.
Falls du aber vor hast zu studieren, dann lass es sein. Denke die Kurse sind nicht so gut.
Konzepte und Prinzipien sind meiner Meinung nach wichtiger.
-
Jaja, ich merke schon da gibt es noch viel zu entdecken in PHP :-)
Jedenfalls habe ich auch ein entsprechendes Tutorial für PHP gefunden, sodass ich dies direkt nachbauen konnte:
http://code.tutsplus.com/tutorials/the-repository-design-pattern--net-35804
Etwas wurde mir dabei aber nicht logisch:
In den Beispielen wird zuerst ein Array aufgeladen mit den Werten nur um anschliessend wieder mit einer super Funktion das richtige Element zu holen. Ich stelle mir dies etwa so vor, wie wenn ich nun mySQL anhänge einfach das SQL-Result als Array auflade und übergebe, nur um anschliessend eine Id wieder zu suchen die ich zuvor schon im Zugriff hatte?
Ich nehme deshalb an, dass folgende Erweiterung dem Sinn der Sache entspricht:
Ich erweitere die Repository um die SQL-Abfragen, sodass ich beispielsweise sagen kann
$repository = new myClassRepository();
$myObjects = $repository->getFromSqlById(8);
Oder wenn ich eine Liste von mehreren Datensätzen möchte:
$repository = new myClassRepository();
$myObjects = $repository->getFromSqlAllWithColour("red");
Die Funktionen getFromSqlById resp. getFromSqlAllWithColour enthalten die Logik zur Abfrage der Daten in der SQL-Datenbank und übergeben die Ergebnisse (jeweils SELECT * .....) der Factory, die nun mein gewünschtes Objekt erstellt, anschliessend wird dieses als ein Array wieder zurückgegeben und befindet sich nun auf $myObjects.
(wobei ich die Funktion getById noch kürzen könnte, sodass diese zuerst prüft ob das Objekt schon einmal von der Datenbank geladen wurde und dann keine Abfrage mehr in der Datenbank startet sondern die Daten aus der persistence holt, glaube das wäre dann caching)
Sodass ich anschliessend nur noch mein $myObjects in der foreach-Schleife durchlaufen kann um die Liste auszugeben.
Für die Übernahme von Xml-Daten steht dann beispielsweise eine Funktion getFromXML("datei.xml") zur Verfügung.
Die Funktion kennt dann das XML-Schema und übergibt der Factory die notwendigen Daten.
Bin ich soweit auf dem richtigen Pfad? -
Deine Frage mit Arrays hab ich nicht verstanden.
Wenn du mir mal die Stelle sagst (keine Zeit und Lust alles zu lesen :P), kann ich es mir mal angucken.
Alles andere was du geschrieben hast, finde ich logisch und macht Sinn.
(wobei ich die Funktion getById noch kürzen könnte, sodass diese zuerst prüft ob das Objekt schon einmal von der Datenbank geladen wurde und dann keine Abfrage mehr in der Datenbank startet sondern die Daten aus der persistence holt, glaube das wäre dann caching)
Persistenz = Datenbank.
Caching = Daten aus dem Cache holen.
Ist die XML da um anzugeben wie die Tabelle/Entität aussieht?
-
Hier der Auszug:
function testItCanFindAllComments() { $repository = new CommentRepository(); $commentData1 = array(1, 'x', 'x', 'x', 'x'); $comment1 = (new CommentFactory())->make($commentData1); $commentData2 = array(2, 'y', 'y', 'y', 'y'); $comment2 = (new CommentFactory())->make($commentData2); $repository->add($comment1); $repository->add($comment2); $this->assertEquals(array($comment1, $comment2), $repository->findAll()); }
require_once __DIR__ . '/Persistence.php'; class InMemoryPersistence implements Persistence { private $data = array(); function persist($data) { $this->data[] = $data; } function retrieve($id) { return $this->data[$id]; } }
Das Beispiel erstellt nun die Comments, fügt diese dann in die Persistenz ein (mit Add()) und lädt sie anschliessend wieder mit findAll();. Und die Angaben werden eben mit einem Array gebildet, doch so hat ja die Fabrik keinen Sinn mehr, die arrays sollten ja in der Repository gebildet und an die Factory übergeben werden. Vielleicht lasse ich mich hier aber auch einfach zu fest wegen der "testgetriebenen Entwicklung" ablenken.
Aber ich glaube dann habe ich das schon richtig verstanden, nur Persistenz habe ich falsch übersetzt, ich hätte nun die dort erklärten Funktionen für Persistenz als Caching verwendet, gut das das noch geklärt ist :-)
Mit dem XML wollte ich aber auf etwas anderes hinaus:
Beispielsweise möchte ich nun ein Objekt aufgrund eines erhaltenen XML bilden und nicht aufgrund eines bestehenden Datenbankeintrages, sodass ich dieses Objekt wieder in die Datenbank speichern kann.
Dann sage ich einfach der Repository: ich hätte gerne ein Objekt mit den Daten aus der XML-Datei xyz.xml, dann lädt meine Repository die Datei xyz.xml und fügt die darin enthaltenen Werte dem Objekt hinzu?
Merci für deine Hilfe :-) -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage