Ausgabe von Bäumen und Navigation mit Nested Sets

Hallo Grille, danke für die schnelle Antwort, aber wenn ich Dich richtig verstehe, dann hat Du das Problem ohne nested Sets gelöst?!
Ich habe leider die Daten als Nested Set gespeichert. Die Ausgabe des Menüs ist Teil eines von mir programmierten CMS, der ganze Admin-Bereich baut darauf auf. Es lässt sich also nicht so leicht umbauen. Ich muss die Daten so auslesen, wie sie gespeichert sind. Und gerade das Auslesen soll ja so vorteilhaft bei nested Sets sein. :rolleyes:

Das Problem tritt auch erst jetzt auf, weil bisher ein anderes Menü eingesetzt wurde, dass diese Sachen per JavaScript/CSS gelöst hat. Jetzt muss ich leider eine Lösung ohne Javascript finden (oder ein JavaScript, das zum Menü passt, bei dem sich Unterordner wieder schließen, wenn man in anderen Unterordnern ist. Ohne JavaScript wäre mir aber lieber.).
 
Hi,

schau Dir mal die Abfrage in diesem Artikel an (die, die den Level mit ausgibt). Liesse sich über diesen Level nicht was machen (also über zusätzliche Bedingungen)?
Klingt sehr theoretisch, ist es auch, ich habe hier nämlich gerade keine Nested Sets-Lösung, an der ich spielen könnte... :)

LG
 
Ja .. genau diesen Artikel hatte ich auch gerade ...

also nochmal Schritt für Schritt ...

Du willst die Ausgabe aller (direkten) Kinderelemente von einem Elternelement .. das wäre dort im Beispiel ... vom Elternelement "Säugetiere" ... die Kinderelemente "Primaten" und "Nagetiere"

Säugetier hat lft 1 ... das nächste was auch angezeit werden muss hat lft 2 wenn dessen rgt NICHT lft +1 (also 3) ist, dann bedeutet es, dass es Enkelkinder gibt. also muss das nächste was angezeigt werden muss das Kinderelement sein, was die lft des letzten rgt+1 ist.

bedeutet: Säugetier ist 1 ... nächste lft ist 2 ist Primat ... Primat hat wieder Kinder die nicht angezeigt werden sollen. Primat hat rgt 7 ... darum suchen wir lft8 (Nagetiere) ... diese werden angezeigt ...

Ich denke jetzt über die SQL-Abfrage nach .. aber kleine Frage nebenbei ... was ist wenn Daten neu eingefügt oder entfernt werden müssen ... geht da nicht der Baum kaputt?
 
Hi,

schau Dir mal die Abfrage in diesem Artikel an (die, die den Level mit ausgibt). Liesse sich über diesen Level nicht was machen (also über zusätzliche Bedingungen)?
Klingt sehr theoretisch, ist es auch, ich habe hier nämlich gerade keine Nested Sets-Lösung, an der ich spielen könnte... :)

LG

Ja, den Artikel kenne ich - sehr gut und informativ. Mein Problem fängt aber genau da an, wo dieser Artikel aufhört. Die Level kann ich ausgeben. Welche zusätzlichen Bedingungen müssen wo hin? Ich bin schon etliche Tage am suchen...
Dort steht z.B. nicht, wie ich die Suche nach den Kindern auf ein Level beschränke. Obwohl ich meine, das System der nested Sets verstanden zu haben, habe ich Schwierigkeiten vorhandene Abfragen zu genüge anzupassen.

Ich denke jetzt über die SQL-Abfrage nach .. aber kleine Frage nebenbei ... was ist wenn Daten neu eingefügt oder entfernt werden müssen ... geht da nicht der Baum kaputt?

Wenn etwas eingefügt oder entfernt wird, dann müssen alle nachfolgenden LFT/RGT-Werte angepasst werden. Das funktioniert bei mir schon (und ist relativ komplex, wenn es sogar ganze Teilbäume sind, die entfernt oder verschoben werden). Die Abfragen kommen aber selten vor. Meistens wird nur ausgelesen, und dabei sind nested Sets schnell, wohl auch, weil keine rekursiven Abfragen benötigt werden. Mit einer Abfrage ist ein ganzer Baum ausgelesen, inkl. Level.

Ja .. genau diesen Artikel hatte ich auch gerade ...

also nochmal Schritt für Schritt ...

Du willst die Ausgabe aller (direkten) Kinderelemente von einem Elternelement .. das wäre dort im Beispiel ... vom Elternelement "Säugetiere" ... die Kinderelemente "Primaten" und "Nagetiere"

Säugetier hat lft 1 ... das nächste was auch angezeit werden muss hat lft 2 wenn dessen rgt NICHT lft +1 (also 3) ist, dann bedeutet es, dass es Enkelkinder gibt. also muss das nächste was angezeigt werden muss das Kinderelement sein, was die lft des letzten rgt+1 ist.

bedeutet: Säugetier ist 1 ... nächste lft ist 2 ist Primat ... Primat hat wieder Kinder die nicht angezeigt werden sollen. Primat hat rgt 7 ... darum suchen wir lft8 (Nagetiere) ... diese werden angezeigt ...
Nach dreimaligem Durchlesen (ist ja schon spät): Ja, so ist die Logik :)
 
Bin auch etwas weiter gekommen :) : Mit dieser netten kleinen Abfrage bekommt man alle Kinder (ohne Enkel) eines Menüpunktes:

Code:
SELECT node.name,
 (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
 FROM menu AS node,
 menu AS parent,
 menu AS sub_parent,
        (
        SELECT node.name, (COUNT(parent.name) - 1) AS depth
        FROM menu AS node,
        menu AS parent
        WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.id = 4
        GROUP BY node.name
        ORDER BY node.lft
        )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node.name
HAVING depth = 1
ORDER BY node.lft

Die ID des Menüpunktes, für das man die Kinder bekommt, ist im Beispiel "4". Wenn man auch den Menüpunkt selbst mit bekommen möchte, lautet die vorletzte Zeile "HAVING depth <= 1".

Quelle: http://www.sydphp.org/presentations/20071101-mptt.html (immer weiter klicken, bis Seite 13)

Damit müsste doch etwas anzufangen sein?! Muss das Script aber noch etwas ausführlicher testen, ob es wirklich immer das liefert, was ich erwarte, komplett überschaubar ist es für mich nicht...
Werde morgen mal weiter forschen und mich melden... aber danke für eure Hilfe!
 
Zuletzt bearbeitet:
Leider klappt es immer noch nicht zufriedenstellend.

Meine Script lautet nun:

Code:
// Erst den Pfad feststellen
$temp = array();
// DISTINCT, falls es mehrere Menüpnukte gibt, die auf die gleiche Seite verweisen. Sonst würde es Mehrfachtreffer geben.
$sql = "SELECT DISTINCT p.id AS id FROM al_menu_d n, al_menu_d p WHERE n.lft BETWEEN p.lft AND p.rgt AND n.link = '".$SCRIPT_NAME."?id=".$_GET['id']."' ORDER BY p.lft";
$result = mysql_query($sql);
while ($row = mysql_fetch_array($result)) $temp[] = $row['id'];

$sql = "SELECT node.id AS id, node.name AS Mname, node.link AS Mlink, node.lft AS lft, node.rgt AS rgt, (COUNT(parent.name) - (sub_tree.level + 1)) AS level
 FROM menu AS node,
 menu AS parent,
 menu AS sub_parent,
        (
        SELECT node.name, (COUNT(parent.name) - 1) AS level
        FROM menu AS node,
        menu AS parent
        WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.id IN (".implode(",",$temp).")
		GROUP BY node.name
        ORDER BY node.lft
        ) AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node.name
HAVING level = 1
ORDER BY node.lft";

$result = mysql_query($sql);

Es verschwinden nun aber immer die Menüpunkte, die gerade angewählt sind. Und außerdem kann ich nicht mehr das korrekte Level feststellen (ist ja jetzt immer 1). Und Untermenüs erscheinen auch nicht. Eigentlich klappt gar nichts :( Was mache ich nur falsch? Muss ich für jede ID aus dem Pfad eine MySQL-Abfrage starten? Ich dachte, das kann ich verbinden...
 
Ich habe jetzt eine Lösung, die wohl funktioniert, nur ausgiebig testen muss ich noch.

Vorgehensweise:
- zuerst Pfad feststellen, wie im vorherigen Script-Beispiel
- dabei verlängere ich ein Array mit jedem Durchlauf, anstatt
Code:
$temp[] = $row['id'];
heißt es nun
Code:
$menuView += getSubTree($row['id']);
getSubTree ist eine Funktion, die die Datenbankabfrage macht, wie sie darunter steht, nur mit einer ID, was ja funktioniert. Dabei lese ich nur die ID und den LFT-Wert als Key aus.
Anschließend sortieren:
Code:
ksort($menuView);
und dann das ganze in die Ursprüngliche Abfrage integriert - voilà:
Code:
... AND node1.id IN (".implode(",",$menuView).")
es klappt!

Jetzt brauche ich zwar statt bisher einer Abfrage 2 + (Anzahl der sichtbaren Menüebenen), aber eine Ebenen-Abfrage benötigt 0.0021 Sekunden. Das hält sich im Rahmen. Und mehr als 3-4 Verschachtelungen in der Tiefe wird es wohl sowieso nicht geben...

Aber falls sich jemand die Mühe macht und einen eleganteren Weg findet, bin ich gerne bereit, es auszuprobieren. Allerdings ist das Script/die Abfragen noch etwas komplizierter als hier dargestellt. Ich muss zwischen unterschiedlichen Menüs hin- und herschalten können und einige Menüpunkte müssen ausgeblendet werden können (falls keine Berechtigung).
 
Zurück