Ich will dieses <option>-NICHT im PHP-Code. Es ist ein HTML-Tag und deswegen soll es auch im HTML-Dokument verbleiben.
Na dann lassen wir das eben dort und schreiben nur den Rest woanders hin
[src=php]foreach($standorte as $standort) {
echo '<option value="'.$standort['standort_id'] .'">' .$standort['ort']. '(' .$standort['strasse'].')</option>';
}[/src]
Die Ausgabe der <option> bleibt wo sie ist, ich habe aber mal das $row durch $standort ersetzt, weil das viel aussagekräftiger ist, wenn kein SQL mehr darüber steht. Außerdem ein anderer Schleifentyp und das Array $standorte in dem eben alle Standorte drin sein müssen.
Wo aber bekommen wir eine solche tolle Variable $standorte her? Schreiben wir uns doch eine Funktion, die sie liefert:
[src=php]$standorte = getStandorte();
foreach($standorte as $standort) {
echo '<option value="'.$standort['standort_id'] .'">' .$standort['ort']. '(' .$standort['strasse'].')</option>';
}[/src]
Eigentlich können wir das gleich inlinen, dann haben wir keine Variable $standorte rumfliegen, die nur einmal benutzt werden würde:
[src=php]foreach(getStandorte() as $standort) {
echo '<option value="'.$standort['standort_id'] .'">' .$standort['ort']. '(' .$standort['strasse'].')</option>';
}[/src]
So Problem gelöst. Naja fast, uns fehlt noch eine Funktion und wir haben noch Code rumliegen, der irgendwo hin muss. Da heißt es Synergien ausnutzen!
[src=php]function getStandorte() {
$db_read = "select * from db_standorte where fk_kunden='".$_SESSION['kunden_id']."' order by ort asc";
$db_result = mysql_query($db_read, $db_connect) or die (mysql_error());
$standorte = array();
while($row = mysql_fetch_assoc($db_result)) {
$standorte[] = $row;
}
return $standorte;
}[/src]
Die Funktion ist im Grunde der alte Code, neu ist nur, dass wir
a) diese Funktion mehrfach verwenden können, ohne den gleichen Code an mehreren Stellen zu haben
b) die Funktion wo ganz anders stehen kann, zB in einer anderen Datei, die dann am Anfang dieser Datei includiert wird.
c) Statt einem echo füllen wir die Variable $standorte und geben diese zurück.
d) ich war so frei auf fetch_assoc umzusteigen. Eigentlich muss das ganze auch auf mysqli, aber das mach ich hier jetzt nicht, das würde dich nur verwirren ;-)
Ich will ja, dass db_standorte variabel gehalten wird, genau wie standort_id, ort und strasse, weil das sind die Dinge, die sich in den Abfrage ändern.
Das klingt nach einem guten Vorschlag, die Funktion ist viel zu präzise, da muss abstrahiert werden.
[src=php]function getStandorte($table) {
$db_read = "select * from $table where fk_kunden='".$_SESSION['kunden_id']."' order by ort asc";
...
[/src]
Das sieht nicht gut aus.. unsere Standorte brauchen ja strasse und ort und standort_id, die bekommen wir aus anderen Tabellen doch gar nicht! Und andere Tabellen haben sicher auch kein ort zum sortieren..
Klar könnten wir weiter abstrahieren. bis wir bei getIrgendwas($tabelle, $spalten, $bedingung, $sortierung) ankommen, aber das wäre doch Blödsinn.
Lass uns das erstmal verschieben und über ein anderes Thema reden..
Ich weis zum Beispiel immer noch nicht, warum ich für [...] OOP nutzen sollte
Weil OOP dabei hilft bei größeren Projekten den Überblick zu behalten wie es funktioniert, wo Änderungen gemacht werden müssen um Bugs zu beseitigen oder neue Features einzubauen. Ein Schlüsselwort ist "Responsibilities".
Zurück zu unserem Problem. Wir brauchen dieses Array $standorte. In wessen Verantwortung liegt es zu wissen, wo (seine) Standorte sind? Laut dem SQL-Query ist es abhängig von "kunden_id", also sind es wohl die Standorte eines Kunden und der Kunde muss wissen wo seine Standorte sind. Wir meinen natürlich nicht den Mensch Kunde, obwohl der es auch wissen sollte, sondern ein Objekt der Klasse "Kunde".
also schreiben wir uns doch einfach mal eine solche Klasse:
[src=php]class Kunde {
public function getStandorte() {
$standorte = array();
// todo SQL Abfrage einfügen
return $standorte;
}
}[/src]
So schon fast fertig. Erstmal zurück zum HTML Code:
[src=php]foreach($kunde->getStandorte() as $standort) {
echo '<option value="'.$standort['standort_id'] .'">' .$standort['ort']. '(' .$standort['strasse'].')</option>';
}[/src]
Dem aufmerksamen Leser fällt auf: Wir haben das Problem bisher nur verschoben: Jetzt brauchen wir plötzlich ein Objekt $kunde und der SQL Code fehlt auch noch. Eines nach dem anderen, erstmal schnell einen Kunden erschaffen, aber nicht irgend einen, sondern natürlich den mit der id aus der Session.
[src=php]$kunde = new Kunde($_SESSION['kunden_id']);[/src]
Dementsprechend müssen wir unsere Klasse anpassen. Die braucht jetzt einen Konstruktor und muss sich die id merken:
[src=php]class Kunde {
protected $id;
public function __construct($id) {
$this->id = $id;
}
public function getId() {
return $this->id;
}
}[/src]
Die ID ist protected. Nur für den Fall, dass wir sie aber mal ausgeben wollen, bekommt sie einen Getter. Die Idee ist Datenkapselung: Einmal erstellt ist die ID eines Kundenobjektes nicht mehr zu ändern. Hantiere ich mit zwei Kunden gleichzeitig, erschaffe ich mir ein zweites Objekt. So kommen die Daten der beiden Kunden nie durcheinander. Jajaja du hast immer nur einen Kunden, schon klar, ich versuch mir hier ein praxisnahes Beispiel aus 5 Zeilen Code abzuleiten, ein bisschen suspension of disbelieve bitte!
Jetzt können wir unsere Funktion weiter ausbauen. Wobei wir hier nun von einer Methode reden sollten, denn Funktionen in einer Klasse nennt man Methoden. Klingt komisch, ist aber so.
[src=php]class Kunde {
protected $standorte;
public function getStandorte() {
if (isset($this->standorte) {
return $this->standorte;
}
$this->standorte = array()
// todo SQL Abfrage einfügen
return $this->standorte;
}
}[/src]
Ich habe mal damit angefangen die Variable $standorte aus der Methode in die Klasse zu ziehen. Damit ist sie jetzt keine Variable mehr, sondern eine Eigenschaft der Klasse. Das ermöglicht uns zu prüfen, ob sie schon gesetzt wurde und wenn ja, dann geben wir sie einfach aus, statt die Datenbank zu belästigen. Wird die Methode getStandorte während einer Anfrage mehrfach aufgerufen, wird die Datenbank nur einmal gefragt.
Aber was ist mit unserem todo? Wird Zeit den alten SQL Code da einfach mal einzufügen, wobei wir gleich auf die Eigenschaft dieses Objekts ($this->id, $this->standorte) benutzen:
[src=php]class Kunde {
public function getStandorte() {
if (isset($this->standorte) {
return $this->standorte;
}
$this->standorte = array()
$db_read = "select * from db_standorte where fk_kunden='" . $this->id . "' order by ort asc";
$db_result = mysql_query($db_read, $db_connect) or die (mysql_error());
while($row = mysql_fetch_assoc($db_result)) {
$this->standorte[] = $row;
}
return $this->standorte;
}
}[/src]
Sieht schon mal ganz ordentlich aus, allerdings funktioniert es nicht. Die Variable $db_connect ist im Scope der Methode nicht definiert.
getStandorte($db_connect) mag ich nicht. Dann muss das HTML ja die Datenbank kennen. Also machen wir daraus eine Eigenschaft, und weil auch die irgendwoher kommen muss, kommt sie mit in den Konstruktor (mein Lieblingsprinzip: störenden Code einfach woanders hin schreiben):
[src=php]class Kunde {
protected $db;
public function __construct($id, $db) {
$this->id = $id;
$this->db= $db;
}
public function getStandorte() {
...
$db_result = mysql_query($db_read, $this->db) or die (mysql_error());
...
}
}[/src]
So wo sind wir nun angekommen und was fehlt noch?
Wir haben die Logik im Kunden (model):
[src=php]class Kunde {
protected $id;
protected $db;
protected $standorte;
public function __construct($id, $db) {
$this->id = $id;
$this->db= $db;
}
public function getId() {
return $this->id;
}
public function getStandorte() {
if (isset($this->standorte) {
return $this->standorte;
}
$this->standorte = array()
$db_read = "select * from db_standorte where fk_kunden='" . $this->id . "' order by ort asc";
$db_result = mysql_query($db_read, $this->db) or die (mysql_error());
while($row = mysql_fetch_assoc($db_result)) {
$this->standorte[] = $row;
}
return $this->standorte;
}
}[/src]
Wir haben die Ausgabe (view):
[src=php]foreach($kunde->getStandorte() as $standort) {
echo '<option value="'.$standort['standort_id'] .'">' .$standort['ort']. '(' .$standort['strasse'].')</option>';
}[/src]
Es fehlt der Ort an dem die Session ausgelesen, die Datenbankverbindung erzeugt, der Kunden erschaffen und alles verschaltet wird (Controller).
Ich vermute du hast schon einen Ort an dem die Datenbankverbindung aufgebaut wird (ganz oben in der Datei) und genau du wirst sicher auch schon prüfen ob $_SESSION['kunden_id'] überhaupt existiert und wenn nicht, wird man zu einem Login geschickt.
Da kommt noch der Aufruf
[src=php]$kunde = new Kunde($_SESSION['kunden_id'], $db_connect);[/src]
hin und fertig ist ein (einfacher, proceduraler) Controller, den du in eine eigene Datei auslagern kannst, der auf allen Seiten eingebunden werden kann, die nur für eingeloggte Kunden sichtbar sind.
Die Klasse Kunde wird sicher noch weitere Methoden bekommen (getName()) weitere Eigenschaften ($name) die für andere Seiten wichtiger sind. Gibt es 20 verschiedene Queries die auf 10 Seiten mit je durchschnittlich 5 Queries verteilt sind, dann haben wir so eine Datei, die alle 20 Queries enthält, statt 10 Dateien mit insgesamt 50, von denen 30 Kopien anderer sind.
Naja fast! Die Klasse heißt "Kunde", da sie alle kundenspezifischen Queries enthalten soll, nicht jedoch solche, die mit Äpfeln oder Birnen zu tun haben.
Man kann die Dinge sicher völlig anders angehen (ich gehe hier jetzt mal nicht Databasemapping, ServiceLocator u.ä. Dinge ein, die ich hier verwenden würde), aber ich hoffe ich konnte dir durch langsames umarbeiten des Codeschnippsels etwas vermitteln ;-)