• Hallo liebe Userinnen und User,

    nach bereits längeren Planungen und Vorbereitungen sind wir nun von vBulletin auf Xenforo umgestiegen. Die Umstellung musste leider aufgrund der Serverprobleme der letzten Tage notgedrungen vorverlegt werden. Das neue Forum ist soweit voll funktionsfähig, allerdings sind noch nicht alle der gewohnten Funktionen vorhanden. Nach Möglichkeit werden wir sie in den nächsten Wochen nachrüsten. Dafür sollte es nun einige der Probleme lösen, die wir in den letzten Tagen, Wochen und Monaten hatten. Auch der Server ist nun potenter als bei unserem alten Hoster, wodurch wir nun langfristig den Tank mit Bytes vollgetankt haben.

    Anfangs mag die neue Boardsoftware etwas ungewohnt sein, aber man findet sich recht schnell ein. Wir wissen, dass ihr alle Gewohnheitstiere seid, aber gebt dem neuen Board eine Chance.
    Sollte etwas der neuen oder auch gewohnten Funktionen unklar sein, könnt ihr den "Wo issn da der Button zu"-Thread im Feedback nutzen. Bugs meldet ihr bitte im Bugtracker, es wird sicher welche geben die uns noch nicht aufgefallen sind. Ich werde das dann versuchen, halbwegs im Startbeitrag übersichtlich zu halten, was an Arbeit noch aussteht.

    Neu ist, dass die Boardsoftware deutlich besser für Mobiltelefone und diverse Endgeräte geeignet ist und nun auch im mobilen Style alle Funktionen verfügbar sind. Am Desktop findet ihr oben rechts sowohl den Umschalter zwischen hellem und dunklem Style. Am Handy ist der Hell-/Dunkelschalter am Ende der Seite. Damit sollte zukünftig jeder sein Board so konfigurieren können, wie es ihm am liebsten ist.


    Die restlichen Funktionen sollten eigentlich soweit wie gewohnt funktionieren. Einfach mal ein wenig damit spielen oder bei Unklarheiten im Thread nachfragen. Viel Spaß im ngb 2.0.

[PHP/MySQL] Karte mit X und Y-Koordinaten erstellen

evillive

EXIL

Registriert
24 Juli 2013
Beiträge
930
Wenn ich ALLEs ins HTML-Dokument schreibe kann man das wenigstens so begründen, dass es in der logischen Reihenfolge schonmal an der richtigen Stelle ist und man eine Funktion nicht erst in einem anderen Dokument suchen muss.

nur weil du es für richtig oder falsch hälst ist es nicht automatisch richtig oder falsch. Die Kunst ist es keine Redundanz zu haben, weil du eine Änderung später an mehreren Stellen durchführen musst. Das Ganze nennt sich DRY-Prinzip. Wenn du mit Funktionen arbeitest und diese gut schreibst, dann kannst du sie an mehreren Stellen wiederverwenden.

Wenn du Logik und Ausgabe von einander trennen willst, dann musst du sogar mit mehreren Dateien arbeiten.
 

Shodan

runs on biochips

Registriert
14 Juli 2013
Beiträge
661
Ort
Citadel Station
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 :D

[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 ;-)
 

theSplit

1998
Veteran Barkeeper

Registriert
3 Aug. 2014
Beiträge
28.573
Puh - da muss ich schnauben, schön wenn es so Schritt für Schritt geht.
Aber es ist doch etwas sehr umfangreich, ich mit meinem Verständnis ziehe mal den Hut. Ist nicht so einfach wie du es darlegst, oder vielleicht doch :)
Zumindest logisch :D
 
Zuletzt bearbeitet:

Cyperfriend

Der ohne Avatar

Registriert
14 Juli 2013
Beiträge
1.123
  • Thread Starter Thread Starter
  • #44
Nur kurz zwischenrein: Ich hatte die letzten beiden Tage keine Zeit. Ich bin dabei das Beispiel von Shodan zu analysieren, nach zu bauen und schlussendlich hoffentlich zu verstehen. Momentan fällt mir das ziemlich schwer - das verstehen. Ist halt sehr abstrakt.
 
Oben