Ich arbeite gerade an einem neuen Framework, dass sehr MVC orientiert ist. Für die Zusammenführung von Daten in einem Array fand ich nichts befriedigendes (für meine Zwecke entsprechendes).
PHP's array_merge funktioniert nur bei eindimensionalen Arrays und array_merge_recursive hängt verschachtelte Daten im falle einer Kollision mit einem numerischen Index an. Das ist für viele Szenarien eine gute Sache, da man keinen Datenverlust bei Kollisionen hat, aber in einer dynamischen Anwendung lässt sich kaum erraten, was sich hinter numerischen Indizes verbirgt.
In meinem Fall soll bei gleichem Schlüssel, der Wert des zweiten Arrays angenommen werden, sofern es sich hier nicht wieder um ein Array handelt. In diesem Fall wird rekursiv traversiert, bis zum Ende des Arrays. Ich würde das als ein natürliches Merging bezeichnen, da es sich erwartungskonformer verhält.
Hier die Klasse Merge.
Man hätte das ganze auch in einer Methode schreiben können und den merge direkt mit den beiden übergebenen Arrays machen können, allerdings möchte ich die Klasse noch um einige Parameter erweitern.
<?php
class Merge {
public static function multiMerge($array1, $array2) {
$res = array();
self::mergeNatural($array1, $res);
self::mergeNatural($array2, $res);
return $res;
}
Ihr könnt diese Klasse natürlich frei verwenden / modifizieren. Sie wird in umfangreicherer Form auch Bestandteil meines neuen Frameworks sein, das ich hoffentlich in den nächsten Wochen fertigstelle.
Ein plattformformübergreifendes Mittel zur Manipulation und Validierung von Strings sind Reguläre Ausdrücke. Den häufigsten Einsatz von Regex findet man wahrscheinlich in der .htaccess um mithilfe des mod_rewrite-Moduls schönere Webadressen (bei google eher unter pretty urls zu finden) ohne hässliche Parameter zu erzeugen. Diejenigen, die kein fertig CMS wie Wordpress oder ähnliches verwenden, müssen früher oder später selbst Hand anlegen um in den Genuss dieses features zu kommen.
Hier ein Auszug aus der .htaccess dieses Blogs:
Die erste Zeile RewriteEngine on sorgt dafür, dass das entsprechende Modul auch aktiviert wird. Die zweite Zeile legt den Ausgangspfad ohne Host fest. Angenommen eure Seite befände sich unter der Adresse http://www.example.com/homepage/, wäre hier /homepage/ einzutragen.
Die folgenden beiden Zeilen
bringen die erste Funktionalität. Oftmals sind Webseiten mit und ohne die Subdomain www erreichbar. Das führt unweigerlich zu Duplicate Content. Google soll diesen Umstand mit einem schlechteren Ranking bestrafen (Ob das wirklich der Fall ist, kann ich nicht mit Sicherheit sagen). Wir legen also eine Condition fest: Sollte die aufgerufene Adresse ohne www beginnen, leiten wir den Benutzer um. Die Umleitung ist in der unteren Zeile geregelt (Rule) mit dem Parameter dieses unter dem http-code 301 zutun, welcher für eine permanente Weiterleitung steht.
Schauen wir uns die nächsten beiden Zeilen an:
Die erste sorgt dafür, dass der Aufruf von www.phunkei.de/blog den Parameter site mit dem Wert blog versieht und page mit dem Wert 0. Es soll also der Blog geladen werden mit der entsprechenden Seitenzahl. Logisch oder? Spannender wird es in der folgenden Zeile, da wir hier wirklich einen Teilstring auslesen. ([1-9]{1}[0-9]*) steht für folgendes: Zeichen zwischen 1 - 9 genau 1-mal, dann wieder Zeichen zwischen 0 - 9 aber beliebig oft. Zulässige Werte bestehen also dem String blog/p zusammenhängend mit ganzen Zahlen von 1 - Unendlich (Wer weiß wieviele Seiten dieses Blog noch fassen wird). Rechts daneben haben wir eine Übersetzung für den Ausdruck, wobei $1 für den ersten Teilstring steht, der in () steht. Gibt es mehrere solcher Teilstrings, die wir für die Übersetzung benötigen, so werden diese logischerweise der Reihenfolge entsprechend mit $1 bis $n referenziert. Damit wäre die Grundsätzliche Frage nach der Arbeitsweise von mod_rewrite geklärt und mit diesem simplen Beispiel lässt sich schon ein Großteil aller Adressen umsetzen. Neben Ziffern sind natürlich auch andere Zeichen möglich.
[a-z] Alle Kleinbuchstaben von a-z
[A-Z] Alle Großbuchstaben von A-Z
[a-zA-Z] Alle Großbuchstaben und Kleinbuchstaben von A-Z
[.] Alle Zeichen
[2] Nur die Ziffer 2
Es gibt auch ganze Zeichenklassen die eine verkürzte Angabe des Wertebereichs ermöglichen. Die meisten davon sind zB. hier zu finden. Es ist aber ebenso wichtig die Anzahl dieser Zeichen zu begrenzen. Dafür stehen folgende Quantoren zur Verfügung.
? Wert muss einmal oder keinmal enthalten sein
+ Wert muss mindestens einmal enthalten sein
* Wert kann beliebig oft enthalten sein, muss aber nicht
{n} Wert muss n-mal enthalten sein
{n, m} Wert muss mindestens n-mal und maximal m-mal enthalten sein
{n,} Wert muss mindestens n-mal enthalten sein, aber auch häufiger ist möglich
Mit diesem Grundlagenwissen lassen sich beinahe alle möglichen Formen für Pretty Urls finden. Die Syntax ist sehr sensibel und kleine Fehler können einen kompletten Ausdruck verändern. Wer seine Regex gerne testen möchte, kann sich einfach eine php-Datei erstellen und die $_GET-Variable ausgeben. Hier sieht man dann wunderbar, welche Werte aus der aufgerufenen Adresse welchem Parameter zugewiesen werden.
Eine klassische Problematik ist das Abbilden von Baumstrukturen in einer SQL Datenbank. Häufige Einsatzgebiete sind Foren, Kommentarsysteme oder Kategorien.
Google findet nur mäßig hilfreiche Lösungsansätze, die sich meistens in eine der folgenden Kategorien einordnen lassen:
Erster Ansatz
Man überlegt sich zu Beginn die Maximale Tiefe des Baums und erstellt dementsprechend viele Felder in der Datenbank, um den gesamten Pfad (alle übergeordneten Knoten) abzubilden.
Nachteil: Limitierte Tiefe.
Lösung: Statt für jede Ebene ein Feld in der Datenbank anzulegen, das ganze serialisiert in ein Textfeld legen. In diesem Fall fehlen die physikalischen Referenzen und dieser Workaround wird etwas "hacky".
Zweiter Ansatz
The Adjacency List Model. Wir notieren pro Datensatz nur die ID des Wurzelelements und sparen eine Menge Platz.
Aber auch dieses Vorgehen birgt einen Nachteil: Das Bauen des gewünschten Baumes wird etwas schrieriger, vorallem wenn
man mit Arrays arbeitet. Um die Komplexität dieses Vorgehens zu umgehen, verwenden alle derartigen Lösungen die ich im Netz fand, einen Query pro Knoten um die Kindelemente zu bekommen. Bei großen Bäumen ist diese Lösung denkbar ineffizient und es wäre wünschenswert, alle Daten in einem Schwung zu holen und diese automatisiert in einen Baum zu verwandeln.
Dritter Ansatz
Mein Ansatz ist nicht ganz neu, auch ich verwende das Adjacency List Model. Aber ich verzichte auf die o.g. Transaktionen und hole mir alle Daten in einem Query. Dafür verwende ich zwei Klassen:
node.php
tree.php
Jede Instanz der Knotenklasse repräsentiert logischerweise einen Knoten. Jeder Knoten kann unendlich viele Knoten enthalten (Kapselung).
<?php
class node {
public $name;
public $id;
public $parent;
public $children;
public function __construct($parent, $id, $name) {
$this->parent = $parent;
$this->id = $id;
$this->name = $name;
$this->children = array();
}
public function addChild($node) {
$this->children[] = $node;
}
}
?>
Die Baumklasse stellt die rekursiven Funktionalitäten für die Baumerstellung bereit.
<?php
class tree {
public $topnode;
private $dataSet;
public function __construct($data) {
$this->topnode = new node(null, 0, "topnode");
$this->dataSet = $data;
$this->createTree($this->topnode);
}
private function createTree($node) {
foreach($this->dataSet as $key => $value) {
if($value['id_parent'] == $node->id) {
$node->addChild(new node($value['id_parent'], $value['id'], $value['name']));
unset($this->dataSet[$key]);
}
}
foreach($node->children as &$c) {
$this->createTree($c);
}
}
public function getTree() {
return $this->topnode;
}
}
?>
Wir übergeben also unsere Datensätze als Array (jeder Datensatz enthält id_parent, id und name oder muss gegebenfalls angepasst werden) und bekommen unseren Baum. Dafür wird ein leeres Topnode erzeugt und die alle Datensätze, die eine id_parent von 0 haben werden dessen Kindelemente. Für jedes Kindelement wird nun unser Datenarray nach Kindern abgesucht und diese werden bei einem Treffer hinzufügt. Das ganze läuft, bis keine Datensätze mehr übrig sind.
Die Klassen sind natürlich noch erweiterbar und bieten nur die grundlegenden Funktionalität, was aber für die meisten Einsatzwecke ausreichen sollte.
Ich habe ein Repo erstellt, damit jeder seine Verbesserungsvorschläge zum Besten geben kann.
Falls jemand die Klassen in einem (auch kommerziellen) Projekt verwenden möchte, darf er das gerne tun.
Während der letzten Arbeiten versuchte ich oftmals auf cookies zu verzichten und dachte an das neue Webstorage feature. Das ist mittlerweile in allen gängigen Browsern implementiert und bietet genügend Platz für Daten. Ein kleiner Minuspunkt ist die Tatsache, dass man nur key - value pairs hinzufügen kann und keine Objekte. Das macht es bei größeren Projekten oftmals schwierig, die Daten geordnet zu speichern.
Eine Möglichkeit besteht natürlich darin, ein Objekt zu serialisieren um eine String Darstellung zu bekommen - bei diesem Gedanken bietet sich die JSON-Notation an. Ich kann meine gesamten Daten unter einem Key speichern und daraus wieder ein Objekt parsen, das alles strukturiert aufbewahrt.
Zu diesem Zweck habe ich ein jQuery Plugin gebastelt. Beim Submit werden alle Daten des selektierten Formulars gespeichert und beim nächsten Aufruf wieder eingefügt. Die Funktion lässt sich auf beliebig viele Formulare innerhalb einer Domain verwenden, solange diese unterschiedliche Namen haben. Um bestimmte Felder von der Prozedur auszuschließen, gibt es den Parameter exclude. Dieser erwartet ein Array von Feldnamen. Ferner lässt sich auch die Art der Speicherung wählen (localStorage oder sessionStorage) und ob ein submit das Formular auch wirklich senden soll. Letzteres ist wohl nur für ajax-requests interessant oder falls noch weitere Routinen auf dem Submit-Event liegen wie Validierung.
Nach unzähligen Monaten habe ich die Zeit gefunden, meinen Blog zumindest soweit fertig zu bekommen, dass er einigermaßen lauffähig ist.
Falls jemand Fehler findet, freue ich mich über jeden Report.
Auch wenn gleich noch ein Beitrag kommt, wünsche ich schonmal einen guten Rutsch ins neue Jahr.