Ach verdammt, ich habe gerade gemerkt, dass im Zip Verzeichnis irgendwas durcheinander gekommen ist. Die KlassenDoku sollte da eigentlich unter doc liegen.....
Naja, egal. Ein Beispiel? Kein Problem:
Nehmen wir diese Tabelle an
Code:
CREATE TABLE menu (
`name` varchar(255) NOT NULL default '',
`nodeID` int(10) unsigned NOT NULL default '0',
`parentID` int(10) unsigned NOT NULL default '0',
`rootID` int(10) unsigned NOT NULL default '0',
`leftID` int(10) unsigned NOT NULL default '0',
`rightID` int(10) unsigned NOT NULL default '0',
`level` int(10) unsigned NOT NULL default '0',
`order_num` int(10) unsigned NOT NULL default '0',
KEY `rootID` (`rootID`),
KEY `leftID` (`leftID`),
KEY `rightID` (`rightID`),
KEY `level` (`level`),
KEY order_num (`order_num`),
PRIMARY KEY ( `nodeID` )
) TYPE=MyISAM;
In deiner PHP Datei musst du natürlich erst mal die Klasse einbinden und dann über die Methode factory ableiten (factory pattern deswegen, um das ganze später mal leichter an PEAR:
B oder PEAR::MDB anpassen zu können). Die Methode erwartet mind. 1 Array mit den 7 wichtigen Daten table, id, parent, root, l, r, level und order_num.
Mit diesen 7 Feldern ist es dann auch problemlos möglich, einfach mal PEAR:
B_NestedSet einzusetzen (wenn es mal bugfreier ist).
Das 2. Array ist für die zusätzlichen Felder gedacht, in unserem Beispiel das Feld name. Man soll ja bei der Erschaffung eines Knotens auch solche Werte gleich mit übergeben können. Ich geh einfach mal davon aus, dass man immer mindestens ein Feld hier drin stehen haben wird, nämlich den Namen des Knotens.
In den Array's stehen die Feldnamen auf der rechten Seite, die Aliasnamen auf der linken. Die Aliasnamen im $params Array sind fest und müssen so lauten.
Beispiel:
PHP:
<?php
/*
* Nested Set Klasse einfügen
*/
require_once("CB_NestedSet.class.php");
/*
* Nested Set Objekt für Typen anlegen
*/
$params = array (
'table' => 'menu',
'id' => 'nodeID',
'parent' => 'parentID',
'root' => 'rootID',
'l' => 'leftID',
'r' => 'rightID',
'level' => 'level',
'norder' => 'order_num'
);
$additional = array (
'name' => 'name'
);
$nestedSet = CB_NestedSet::factory($params, $additional);
?>
Man hat nun ein NestedSet Objekt.
Beim einfügen eines neuen Knotens muss man unterscheiden, ob man einen Wurzelknoten oder einen Kindknoten erschaffen will.
Man muss die zusätzlichen Felder die zu füllen sind per Array übergeben:
PHP:
$additional = array (
"name" => "Name des Knotens"
);
Für Wurzelknoten nimmt man:
PHP:
$id = $nestedSet->createRootNode($additional);
Bei Kindknoten nimmt man:
PHP:
$id = $nestedSet->createSubNode($parentID, $additional);
Natürlich ist auch löschen möglich:
PHP:
$id = $nestedSet->deleteNode($nodeID);
So...nun aber zu den Schmankerln meiner Klasse: den Abfrage Methoden.
Es werden ja jetzt schon einige Methoden geboten. Jede Methode die nur einen Knoten abfragt liefert ein Array mit den Feldwerten direkt zurück. Wenn es keinen Knoten gab, wird immer false zurück geliefert.
Beispiel:
PHP:
$node = $nestedSet->getNode($id);
$nodeID = $node[nodeID];
$nodeName = $node[name];
Wie man sieht, muss man die Felder so ansprechen, wie die Tabellennamen in der Datenbank lauten. Im Gegensatz dazu muss man beim Erzeugen der Knoten beim Additional Array immer das als Feldnamen angeben, was man beim Ableiten der Klasse auf der linken Seite angegeben hat.
Man kann dieses Verhalten aber ändern:
PHP:
$node = $nestedSet->getNode($id, true);
$nodeID = $node[id];
$nodeName = $node[name];
Als 2. Parameter wurde hier die Verwendung von Aliasnamen bei den zurück gelieferten Einträgen als Feldnamen auf true gesetzt. Man kann erkennen, dass man nicht mehr nodeID angeben muss, um an die ID zu kommen, sondern den Alias id.
Wenn man eine Methode aufruft, die mehr als einen Knoten zurückliefern kann, bekommt man immer ein Array, dessen erstes Level die ID enthält und im 2. Level die Werte dieses Knotens. Die Knoten werden in der richtigen Reihenfolge geliefert.
Beispiel:
PHP:
$pathNodes = $nestedSet->getPath($id, true);
if($pathNodes != false) {
$multiple = false;
foreach($pathNodes as $k => $v) {
if($multiple) {
echo " >> ";
}
echo $v[name];
$multiple = true;
}
}
In diesem Beispiel wird der Pfad von der Wurzel zu einem bestimmten Knoten abgefragt und ausgegeben
Noch ein Beispiel mit Einrückung:
PHP:
$branchNodes = $nestedSet->getBranch($id, true);
if($branchNodes != false) {
foreach($branchNodes as $k => $v) {
for($z = 1; $z <= $v[level]; $z++) {
echo " ";
}
echo $v[name].", ID: ". $v[id] ."<br>";
}
}
Will man beispielsweise in einem Forum nur alle Topics ausgeben, genügt folgendes:
PHP:
$rootNodes = $nestedSet->getRootNodes($id, true);
if($rootNodes != false) {
foreach($rootNodes as $k => $v) {
echo $v[name].", ID: ". $v[id] ."<br>";
}
}
Einrückung wird ja hier nicht benötigt, da Rootnodes ja immer level = 1 haben. Aber ich entdecke gerade ein kleines Problem: will man die Sortierung nach Datum vornehmen (oder Datum des letzten Eintrags), kann man das bisher noch nicht ..... In der nächsten Version liefere ich eine Option zur Modifikation des SQL Statements nach.....
Naja, und so kann man sich durch die Methoden arbeiten.
getDescendants() gibt alle Kinder eines Knotens aus, exklusive dem Knoten selber.
getBranch() macht das ganze inklusive des Knotens.
getSiblings() liefert alle Geschwister
getParent() gibt die direkte Mutter zurück, getParents() gibt alle Eltern zurück (naja, also Opa und Uropa ....
)
Und die Methode mit der man alle Voränger (also den Stammbaum) eines Knotens bekommt, die habe ich ja hier erfragt, aber noch immer nicht zusammenbekommen. Aber ich schaff das noch ....
So ...
kleine Erklärung dieser Klasse .... Wie gesagt, ist die erste Version. Die Versionsnummer resultiert aus der CVS ID, keine Ahnung warum die so hoch ist. Aber ich folge jetzt einfach der ID
In einer der nächsten Versionen werde ich noch weitere Extras einbauen. Man kann dann einer get Methode ein BitFeld übergeben, dass einige Optionen markiert. Beispielsweise kann man dann angeben, ob man bei jedem Knoten zurück geliefert haben will, wie groß die Anzahl der Geschwister ist oder wieviele Nachkommen ein Knoten hat (wieder: Berechnung erfolgt direkt in der Datenbank durch den Query).
Ciao, Jörg