Ok! Machen wir weiter.
Danke erstmal an alle, die mir geschrieben haben. Vor allem an die, welche sich so intensiv mit dem Thema auseinander gesetzt und mich mit ihren Fragen bis zur Verlegenheit gebracht haben. 
Von hier aus auch einen Gruß an die Leute von (noch) außerhalb des Forums. Besonders an den "Zerstoiber". Ich hab die statements nochmal bearbeitet und umgeschrieben. Damit dürften sich die Fragen vielleicht schon erledigt haben. Ansonsten ... icq. 
Aber jetzt gehts los!
Wir brauchen eine Datenbank. Also brauchen wir einen Zugang (die Profis "hören" hier mal bitte weg!). Dafür legen wir alle Zugangsdaten in eine nestedSets.inc.php und diese in unser erstes Unterverzeichnis /inc.
PHP-Code:
$DB['host'] = "localhost";
$DB['user'] = "root";
$DB['pass'] = "";
$DB['name'] = "nested_sets";
Und die wird jetzt in die nestedSets_fnc.inc.php eingebunden. Das sieht dann so aus:
PHP-Code:
require_once "inc/nestedSets.inc.php";
/** connect zur datenbank **/
function connectDB() {
global $DB;
$linkID = mysql_connect($DB['host'],$DB['user'],$DB['pass']) or
die("Hilfe, kann Server nicht erreichen!");
mysql_select_db($DB['name']) or
die("keine Datenbank erreichbar, Käpt´n!");
return $linkID; // ja dennis, ich weiss, dass man das nicht muss! *fg*
}
Ab jetzt wirds denk ich wieder für alle interessant.
Als erstes lesen wir die Navigation aus. Erstmal nur auslesen. Wir bringen das Ganze später in Form.
PHP-Code:
/** navigation aufbauen **/
function getNavi() {
$sql = "SELECT DISTINCT root_id AS roots
FROM node";
$result = mysql_query($sql, connectDB());
$count = mysql_num_rows($result);
while($row = mysql_fetch_array($result)){
$root[] = $row[0];
} // while
$sql= "SELECT n.*,
floor((n.rgt-n.lft-1)/2) AS childs,
count(*)+(n.lft>1) AS level,
((min(p.rgt)-n.rgt-(n.lft>1))/2) > 0 AS lower,
(((n.lft-max(p.lft)>1))) AS upper
FROM node n, node p ";
if ($count > 1) { // fuer mehrere wurzeln
$sql.= "WHERE n.lft BETWEEN p.lft AND p.rgt
AND (p.root_id = n.root_id)
AND (p.node_id != n.node_id OR n.lft = 1)
AND (p.root_id IN (".implode(",",$root)."))
GROUP BY n.root_id,n.node_id
ORDER BY n.root_id,n.lft";
} elseif ($count == 1) { // ... oder eben nur eine
$sql.= "WHERE n.lft BETWEEN p.lft AND p.rgt
AND (p.root_id = n.root_id)
AND (p.node_id != n.node_id OR n.lft = 1)
AND (p.root_id = ".$root[0].")
GROUP BY n.node_id
ORDER BY n.lft";
} else { // alle wurzeln ... der defaulf-fall eben
$sql.= "WHERE n.lft BETWEEN p.lft AND p.rgt
AND (p.root_id = n.root_id)
AND (p.node_id != n.node_id OR n.lft = 1)
GROUP BY n.root_id,n.node_id
ORDER BY n.root_id,n.lft";
}
$result = mysql_query($sql, connectDB());
return $result;
}
Ganz schöner Brocken. Ich hab mal wieder auf floor zurückgegriffen. Der Rest müßte soweit drinstehen (siehe Kommentare).
Bringen wir das Ganze jetzt in eine Form. Und wundert Euch bitte nicht über die "seltsamen" Formatierungen. Das Rätsel löse ich später. Danke auf jeden Fall an Jan und bereits hier möchte ich auf seine Urheberschaft an dem Javascript hinweisen. Von ihm bekommt mein PHP-Scrpit sein Mojo. 
PHP-Code:
/** navigation ausgeben **/
function showNavi() {
$result = getNavi();
// diese funktion ist dermassen unsauber *ganzrotwerd*
// die muss unbedingt noch ueberarbeitet werden!
if ($_REQUEST['site'] == "admin"):
$tree = "<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\"><form action=\"\" method=\"post\">";
$tree.= "<tr id=\"top\"><th>Wurzel</th>";
$tree.= "<th>Name</th><th>Ebene</th>";
$tree.= "<th>Verschieben</th><th>Löschen</th>";
$tree.= "<th>Einfügen</th>";
$tree.= "<th>Editieren</th></tr>";
while($row = mysql_fetch_array($result)) {
($race/2 == floor($race/2)) ? $class = "line1" : $class = "line2";
$tree.= "<tr><td align=\"center\" class=\"".$class."\">".$row[1]."</td>";
$tree.= "<td class=\"".$class."\">".$row[2]."</td>";
$tree.= "<td align=\"center\" class=\"".$class."\">".$row[6]."</td>";
$tree.= "<td align=\"center\" class=\"".$class."\"><a href=\"".$PHP_SELF."?site=admin&action=moveup&id=".$row[0]."\" onfocus=\"blur()\"><img src=\"img/up.png\" border=\"0\"></a> ";
$tree.= "<!-- <a href=\"".$PHP_SELF."?site=admin&action=movedown&id=".$row[0]."\" onfocus=\"blur()\"><img src=\"img/down.png\" border=\"0\"></a> --></td>";
$tree.= "<td align=\"center\" class=\"".$class."\"><a href=\"".$PHP_SELF."?site=admin&action=del&id=".$row[0]."\" onfocus=\"blur()\"><img src=\"img/del.png\" border=\"0\"></a></td>";
$tree.= "<td align=\"center\" class=\"".$class."\"><a href=\"".$PHP_SELF."?site=admin&action=insert&id=".$row[0]."\" onfocus=\"blur()\"><img src=\"img/insert.png\" border=\"0\"></a></td>";
$tree.= "<td align=\"center\" class=\"".$class."\"><a href=\"".$PHP_SELF."?site=admin&action=edit&id=".$row[0]."\" onfocus=\"blur()\"><img src=\"img/edit.png\" border=\"0\"></a></td></tr>";
$race+=1;
} // while
$tree.= "</form></table>";
else:
// bei diesem teil darf ich mich fuer die hilfe von jan bedanken
$level = 1;
$tree.= "<ul id=\"NewTree\">";
while($row = mysql_fetch_array($result)) {
if ($row['level'] > $level)
$tree.= "<ul>";
else if ($row['level'] < $level)
$tree.= str_repeat("</li></ul>", $level-$row['level']);
else if ($level > 1)
$tree.= "</li>";
$tree.= "<li value_id=\"".$row['node_id']."\" level=\"".$row['level']."\" input_id=\"".$row['root_id']."\" productgroupname=\"\" input_type=\"\"><span>".$row[2]."</span>";
$level = $row['level'];
} // while
$tree.= str_repeat("</ul>", $level-1);
endif;
return $tree;
}
So, kommen wir jetzt zu den Manipulationen. Fügen wir zuerst neue Knoten ein. Das Script funzt sowohl für Knoten, Blätter und Wurzel.
PHP-Code:
/** fuegt einen neuen knoten ein **/
function nodeIns($id,$name) {
if (!empty($id)) $node = getNode($id);
if (!empty($id)) {
if ($id == $node[0]) {
if ($brother == "left"): // noch ungetestet!
$sql = "UPDATE node
SET lft = lft + 2
WHERE root_id = ".$node[1]."
AND lft > ".$node[4]."
AND rgt >= ".$node[4]."";
$result = mysql_query($sql, connectDB());
$sql = "UPDATE node
SET rgt = rgt + 2
WHERE root_id = ".$node[1]."
AND rgt >= ".$node[4]."";
$result = mysql_query($sql, connectDB());
$sql = "INSERT INTO node (root_id,payload,lft,rgt)
VALUES (".$node[1].",'".$name."',".$node[3].",".($node[3]+2).")";
$result = mysql_query($sql, connectDB());
else: // bruder rechts
$sql = "UPDATE node
SET lft = lft + 2
WHERE root_id = ".$node[1]."
AND lft > ".$node[4]."
AND rgt >= ".$node[4]."";
$result = mysql_query($sql, connectDB());
$sql = "UPDATE node
SET rgt = rgt + 2
WHERE root_id = ".$node[1]."
AND rgt >= ".$node[4]."";
$result = mysql_query($sql, connectDB());
$sql = "INSERT INTO node (root_id,payload,lft,rgt)
VALUES (".$node[1].",'".$name."',".$node[4].",".($node[4]+1).")";
$result = mysql_query($sql, connectDB());
endif;
}
} else {
$sql = "SELECT max(root_id) as maxroot
FROM node";
$result = mysql_query($sql, connectDB());
$row = mysql_fetch_row($result);
$root_id = $row[0];
$sql = "INSERT INTO node (root_id,payload,lft,rgt)
VALUES (".($root_id+1).",'".$name."',1,2)";
$result = mysql_query($sql, connectDB());
}
}
Damit wir das Ganze nachher umbenennen können (hier wird als default-Wert "neuer Knoten" als Name eingesetzt) hier der Update eines Knoten.
PHP-Code:
function nodeUpdate($id,$name) {
$sql = "UPDATE node
SET payload = '".$name."'
WHERE node_id = '".$id."'";
$result = mysql_query($sql, connectDB());
}
Und weil das so klein und niedlich war gleich unseren Helfer hinterher. Einige von Euch haben ihn vielleicht schon vermißt.
PHP-Code:
/** infos ueber den knoten **/
function getNode($id) {
// alle wichtigen angaben zu einem knoten (root_id,
// node_id, payload, lft, rgt und die kinderchen)
$sql = "SELECT *, floor((rgt - lft)/2) AS childs
FROM node
WHERE node_id = ".$id."";
$result = mysql_query($sql, connectDB());
$row = mysql_fetch_row($result);
return $row;
}
Was wir eingefügt haben möchten wir auch wieder löschen. Oder das, was andere geschrieben haben ... nicht wahr Paul! 
PHP-Code:
/** knoten loeschen - mit und ohne kinder **/
function nodeDel ($id) {
$node = getNode($id);
if (($node[4]==$node[3]+1)): // wenn es ein blatt ist
$sql = "UPDATE node
SET lft = lft - 2
WHERE lft > ".$node[3]."
AND root_id = ".$node[1];
$result = mysql_query($sql, connectDB());
$sql = "UPDATE node
SET rgt = rgt - 2
WHERE rgt > ".$node[4]."
AND root_id = ".$node[1];
$result = mysql_query($sql, connectDB());
$sql = "DELETE
FROM node
WHERE node_id = ".$id;
$result = mysql_query($sql, connectDB());
else: // ... oder ein knoten
// VORSICHT! diese funktion loescht alle
// knoten und blaetter im zweig mit!
$sql = "DELETE
FROM node
WHERE rgt <= ".$node[4]."
AND lft >= ".$node[3]."
AND root_id = ".$node[1];
$result = mysql_query($sql, connectDB());
$sql = "UPDATE node
SET rgt = rgt - ".($node[4]-$node[3]+1)."
WHERE rgt > ".$node[4]."
AND root_id = ".$node[1];
$result = mysql_query($sql, connectDB());
$sql = "UPDATE node
SET lft = lft - ".($node[4]-$node[3]+1)."
WHERE lft > ".$node[4]."
AND root_id = ".$node[1];
$result = mysql_query($sql, connectDB());
endif;
}
Und seid bitte vorsichtig! Ich habs schon geschrieben: das Script löscht ganze Zweige ohne Nachfrage mit allem drum und dran.
Bleibt erstmal nur noch das Verschieben von Knoten. Hier wird zunächst nur nach oben gerückt.
PHP-Code:
/** knoten verschieben **/
function nodeMove($id,$direction) {
$sql = "SELECT n1.root_id,
n1.payload n1payload, n1.lft n1lft, n1.rgt n1rgt,
n2.payload n2payload, n2.lft n2lft, n2.rgt n2rgt
FROM node AS n1
LEFT OUTER JOIN
node AS n2
ON (n1.lft = (n2.rgt+1)
AND n1.rgt > n2.rgt
AND n1.root_id = n2.root_id)
WHERE n1.node_id=".$id."";
$result = mysql_query($sql, connectDB());
$nodes = mysql_fetch_row($result);
$result = mysql_query($sql, connectDB());
if (!$nodes[5]) $error = "<p>Kein übergeordneter Knoten vorhanden!</p>";
$desc = (($nodes[2]-$nodes[5]));
$inc = (($nodes[3]-$nodes[6]));
$sql = "UPDATE node
SET lft = lft + IF(lft<".$nodes[2].",".$inc.",-".$desc."),
rgt = rgt + IF(rgt<".$nodes[2].",".$inc.",-".$desc.")
WHERE root_id = ".$nodes[0]."
AND lft >= ".$nodes[5]."
AND rgt <= ".$nodes[3]."";
$result = mysql_query($sql, connectDB());
return $error;
}
Wer das Ganze in Funktion testen möchte: hier läuft es schon (im Augenblick noch IE only ... zumindest das javascript). Bitte zum Ein- und Ausklappen der Verzweigungen neben die Knoten klicken (werdet Ihr schon finden
).
Ich pack die Sachen jetzt noch zusammen und leg sie dann zum downloaden und testen bereit.
Um noch ein paar Sachen zu erläutern:
Die Idee mit den Variablen innerhalb der Statements hab ich erstmal aufgegeben (nehme ich vielleicht für eine zukünftige Klasse wieder auf). Während der Scripttest zu diesem Workshop hatte ich zu viele Fehlerausgaben mit den Variablen und nicht genügend Zeit, diesen auf den Grund zu gehen.
Die Frage nach dem "Aufwärtsschieben" der Knoten muß ich auch noch schuldig bleiben. Mein Testscript macht das zwar bereits, verdirbt dabei allerdings sämliche linke und rechte Werte. 
Das Löschen bräuchte noch einen Warnhinweis und eine Bestätigung. Und dann ist die Funktion zum Aufbau der Navigation noch eine Katastrophe.
So, jetzt fällt mir nix mehr ein. Probierts aus und dann ... schlagt mich ans Kreuz. 
Einen lieben Gruß Euch allen
Euer Hardy