Ausgabe von Bäumen und Navigation mit Nested Sets

qsrs

Erfahrenes Mitglied
Hallo,

mache gerade die ersten Übungen und Gehversuche mit Nested Sets. Habe ein kleines Problem und anschließend noch eine Frage. Ich versuche in einem Select-Feld den Baum auszugeben, der in einer DB mittels Nested Sets gespeichert wurde. Bei einfacher Ausgabe funktioniert alles problemlos. Nur wenn ich es in einem Select-Feld versuche, klappt es nicht. Hier die Funktion und mein Versuch:
PHP:
 <?php $root = "Food";
   // retrieve the left and right value of the $root node 
   $result = mysql_query('SELECT lft, rgt FROM tree '. 
                          'WHERE title="'.$root.'";', $dbconnect); 
   $row = mysql_fetch_array($result); 
   // start with an empty $right stack 
   $right = array(); 
   // now, retrieve all descendants of the $root node 
   $result = mysql_query('SELECT title, lft, rgt FROM tree '. 
                          'WHERE lft BETWEEN '.$row['lft'].' AND '. 
                          $row['rgt'].' ORDER BY lft ASC;', $dbconnect); 
   // display each row 
   while ($row = mysql_fetch_array($result)) { 
       // only check stack if there is one 
       if (count($right)>0) { 
           // check if we should remove a node from the stack 
           while ($right[count($right)-1]<$row['rgt']) { 
               array_pop($right); 
           } 
       } 
       // display indented node title 
       echo str_repeat(" &nbsp; ",count($right))."<option value=\"".$row['rgt']."\">".$row['title']."</option>"; 
       // add this node to the stack 
       $right[] = $row['rgt']; 
   } 
} 
 
?>
<form id="form1" name="form1" method="post" action="">
  <select name="select" size="5" multiple="multiple">
    <?php display_tree($root); ?>
  </select>
</form>

Mein Problem taucht hier auf:
PHP:
 echo str_repeat(" &nbsp; ",count($right))."<option value=\"".$row['rgt']."\">".$row
Ich möchte, dass in dem Select-Feld auch eine Baumstruktur angezeigt wird. Das versuche ich durch Einrücken mittels &nbsp;. Jetzt wird allerdins folgender Code erzeugt:
HTML:
<option value="20">Food</option> &nbsp; <option value="13">Fruit</option> &nbsp;  &nbsp; <option value="8">Red</option> &nbsp;  &nbsp;  &nbsp; <option value="5">Cherry</option> &nbsp;  &nbsp;  &nbsp; <option value="6">Strawberry</option> &nbsp;  &nbsp; <option value="12">Yellow</option> usw.
Korrekterweise müsste es aber so aussehen:
HTML:
<option value="20">&nbsp; Food</option>
aber ich weiß im Moment nicht, wo ich ansetzen soll.

Nun zu meiner zweiten Frage. Ich versuche unabhängig davon eine simple Navigation zu realisieren. Ich möchte, dass immer nur der Knoten, ohne Kinder ausgegeben wird. D.h. ich wähle die Hauptkategorie, und sehe beispielsweise zwei Unterkategorien, nicht aber die Unterkategorien dieser, sondern erst, wenn ich darauf klicke, also erneut eine Abfrage starte. Meine Frage ist nun, geht das, wenn ich in der DB nur eine id und die Spalte rgt und lft habe? Oder muss eine weitere Spalte, z.B. mit einer parent_id her, damit ich rekursiv abfragen kann? Vielen Danke für eure Hilfe.
 
Um dieses zu erhalten:
HTML:
<option value="20">&nbsp; Food</option>

versuche doch das mal:
PHP:
echo "<option value=\"".$row['rgt']."\">".str_repeat(" &nbsp; ",count($right)).$row;
 
Vielen Dank, das wars.

Nun noch zur zweiten Frage, vielleicht sollte ich sie noch einmal besser formulieren. Ich habe eine Tabelle, die wiefolgt aussieht:

27380attachment.jpg

Nun die Frage. Ist es mit dieser Struktur möglich, eine Link-Navigation zu erstellen, in der immer nur ein Knotenpunkt angezeigt wird? Also in diesem Beispiel erst einmal nur Food. Wenn ich auf Food klicke wird erst einmal nur Fruit und Meat angezeigt, wenn ich dann auf Fruit klicke, soll nur Red und Yellow angezeigt werden usw. Ist das möglich, oder benötige ich eine parent_id, so dass ich dies rekursiv abfragen kann/muss? Vielen Dank für eure Hilfe.
 
Für was stehen die Feldnamen lft und rgt? Ich erkenn da keine Struktur, vielleich bin ich auch nur zu blöd :-( .
Ich hatte es bei meiner Kategorieverwaltung so gemacht, das ich eine ID und die ID der Mutterkategorie hatte. Mit einer einfachen rekursiven Funktion konnte man ohne Probleme damit arbeiten.
 
Das ist das Nested Sets Model.
Ich hatte es bei meiner Kategorieverwaltung so gemacht, das ich eine ID und die ID der Mutterkategorie hatte. Mit einer einfachen rekursiven Funktion konnte man ohne Probleme damit arbeiten.
So hatte ich es ursprünglich auch gemacht, es gibt dabei aber wohl Einschränkungen, v.a. in der Performance. Rekursive Abfrage heißt ja für jede Aktion eine Datenbankabfrage, und das führt bei großen Hierarchien zur Auslastung der DB. Ich kann mir vorstellen, dass bei einem stark frequentierten System, mit vielen hundert Usern gleichzeitig, das schon ins Gewicht fällt. Meine Frage ist nun, ob ich mit Nested Sets immer einzelne Knotenpunkte abfragen kann, ohne die Childs auszugeben.
Wenn ich auf Food klicke wird erst einmal nur Fruit und Meat angezeigt, wenn ich dann auf Fruit klicke, soll nur Red und Yellow angezeigt werden usw. Ist das möglich, oder benötige ich eine parent_id, so dass ich dies rekursiv abfragen kann/muss? Vielen Dank für eure Hilfe.
 
Das Preordered-Tree-Traversal-Modell (im deutschen Sprachraum wohl eher als „Nested Set“ bekannt) ist zwar in der Verwaltung aufwändiger, dafür sind aber die Abfragen der Kindelemente wesentlich einfacher.
Denn die Werte „lft“ und „rgt“ beschreiben die Grenzen aller Kindelemente eines Knotens. Im obigen Beispiel haben alle Kindelemente des „Food“-Knotens einen „lft“- und „rgt“-Wert zwischen 1 und 20, die des „Fruit“-Knotens zwischen 2 und 13, die des „Meat“-Knotens zwischen 14 und 19 und so weiter.
 
Ist es dann bei der für mich gewünschten Navigation sinnvoll, eine parent_id mitzuspeichern um dann rekursiv abzufragen und zu Navigieren, oder kann man aufgrund der lft und rgt Werte bzw. der id zumindest eine rekursive Navigation erreichen? Die einzige Frage, die noch offen für mich ist, ist die, ob das mit dieser Struktur (id, lft, rgt) möglich ist, oder ob ich noch eine weitere Spalte mit einer parent_id benötige.
 
Die zweite Frage des Threadstarters trifft auch mein Problem, daher dachte ich mir: warum einen neuen Thread erstellen? ;) Also:

Ich möchte eine Menüstruktur darstellen. Das komplette Menü sieht z.B.so aus (die IDs könnten natürlich auch durcheinander sein):

- Fahrräder (id 1)
- Autos (id 2)
-- BMW (id 3)
--- 3er BMW (id 4)
--- 5er BMW (id 5)
-- Mercedes (id 6)
- Motorräder (id 7)
- Baumaschinen (id 8)
-- Bagger (id 9)
- Busse (id 10)

Wenn ich den Menüpunkt "Autos" aufrufe müssen alle Menüpunkte der ersten Ebene sichtbar sein, sowie eine ( ! ) Ebene unterhalb "Autos", also "BMW" und "Mercedes", nicht aber "3er BMW" und "5er BMW".

Oder anders ausgedrückt: Alles, was dem gleichen Obermenü untergeordnet ist, und das rekursiv, inkl. Level 1 soll sichtbar sein, sowie alles, was ggf. 1 Level unter dem aktuellen Menüpunkt ist.

Wie bekommt man das in ein oder zwei MySQL-Abfragen hin? Ich denke, mit dem Problem werden sich schon viele befasst haben, es ist ja eine gängige Darstellungsart.

Mein Denkansatz: Mit folgendem Code bekomme ich alle direkten Eltern des aktuellen Punktes:
Code:
SELECT p. * , n.lft
FROM menu n, menu p
WHERE n.lft
BETWEEN p.lft
AND p.rgt
AND n.id =4
ORDER BY p.lft

...liefert z.B. "Autos (id 2) > BMW (id 3) > 3er BMW (id 4)".

Jetzt bräuchte ich zu jedem Punkt nur noch die Kinder mit den gleichen Eltern, also innerhalb der LFT/RGT-Werte und mit dem gleichen Level. Anschließend noch herausbekommen, ob der aktuelle Menüpunkt Kinder hat (IF RGT-LFT>1) und diese auslesen (wie geht das, wenn man nur ein Level darunter braucht?).

Es gibt auch eine PEAR-Klasse, aber dort werde ich auch nicht schlau. (Möchte aber auch nicht alles einbinden, nur um das Menü auszugeben, alles andere (Punkte löschen/erzeugen) funktioniert ohne PEAR. und ich habe zu wenig (bzw. keine) OOP-Erfahrung.)

Die Klasse: http://pear.php.net/manual/de/package.database.db-nestedset.php

Ausschnitt Kinder auslesen:
Code:
        $sql = sprintf('SELECT %s %s FROM %s %s
                        WHERE %s.%s=%s AND %s.%s=%s+1 AND %s.%s BETWEEN %s AND %s %s %s
                        ORDER BY %s.%s ASC',
        $this->_getSelectFields($aliasFields), $this->_addSQL($addSQL, 'cols'),
        $this->node_table, $this->_addSQL($addSQL, 'join'),
        $this->node_table, $this->flparams['rootid'], $parent['rootid'],
        $this->node_table, $this->flparams['level'], $parent['level'],
        $this->node_table, $this->flparams['l'], $parent['l'], $parent['r'],
        $this->_addSQL($addSQL, 'where', 'AND'),
        $this->_addSQL($addSQL, 'append'),
        $this->node_table, $this->secondarySort);

Für Hilfen oder Lösungsansätze bin ich dankbar. Aber bitte keine Links zu allgemeinen Tutorials für Nested Sets, die habe ich durch und bin nicht weiter genkommen. :(
 
Zuletzt bearbeitet:
Hallo .. ich hoffe ich verstehe dich richtig und ich greife mit meiner Antwort nicht daneben.

Ich will in meinem Projekt das so Lösen (mit Branchen)

Ich will in MySQL nur eine Tabelle mit meinem Branchen machen in welcher die ID, der Branchenname und die parentID gespeichert werden.

z.B.
PHP:
ID    Name       parentID
1     A          keine
2     B          keine
3     C          1
4     D          1
5     E          4
6     F          2
 
Würde dann bedeuten
 
A
   C
   D
      E
 
B
   F

Und extra in einer MySQL-Tabelle dann die Einträge die dann der jeweiligen ID des Menüpunktes zugeordnet werden

Und bei der Abfrage guckst du einfach welche ID der eben angeklickte Button hatte und suchst in der SQL-Abfrage alle Punkte herraus die die selbe parentID haben...
 
Zuletzt bearbeitet:
Zurück