PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Nested Sets - Weiterverarbeitung


MadMan2k
2004-12-27, 16:50:21
das Prinzip der Speicherung hab ich glaub ich so weit verstanden, bloß was mach ich mit den Daten, wenn ich sie erstmal habe?

Ich krieg z.B. folgende Tabelle für ein Menü:

Name | Tiefe
---------+--------
root | 1
artikel | 2
artikel1 | 3
artikel2 | 3
spiele | 2
spiel1 | 3
spiel2 | 3


daraus soll dann folgendes Menü entstehen (HTML verschachtelte Listen):


root
|
`- artikel
| |
| `- artikel1
| |
| `- artikel2
|
`- spiele
|
`- spiel1
|
`- spiel2


wie müsste hierfür der Code aussehen und bräuchte ich dazu auch die Anzahl der Kindknoten?

ne0
2004-12-27, 19:24:59
habe ich mal gemacht, schau mal


<?php
$sql = 'SELECT node1.payload,
IF ( node1.node_id = node1.root_id,
round( (node1.rgt - 2) / 2, 0),
round( ( (node1.rgt - node1.lft - 1) / 2), 0)
) AS children,
COUNT(*) AS level

FROM node2 AS node1,
node2 AS node2

WHERE node1.root_id = 1
AND node2.root_id = 1

AND node1.lft BETWEEN node2.lft AND node2.rgt

GROUP BY node1.LFT;
';

$query = mysql_query($sql);

$lvl = 0;

while($row = mysql_fetch_array($query)) {
if($row[level] > $lvl){
echo("<ul>\n");
echo("\t<li>$row[payload]</li>\n");
}
if($row[level] < $lvl){
echo("</ul>\n");
echo("\t<li>$row[payload]</li>\n");
}
if($row[level] == $lvl){
echo("\t<li>$row[payload]</li>\n");
}
$lvl = $row[level];
}
for($i=$lvl;$i>0;$i--){
echo("</ul>\n");
}
?>

MadMan2k
2004-12-27, 19:42:40
danke, das ist schonmal ein Ansatz - allerdings funktioniert die Lösung soweit ich es überblicke nur mit zwei Ebenen und gibt außerdem keine validen Listen nach XHTML aus.

Diese müssen so folgendermaßen geschachtelt sein:


<ul>
<li></li>
<li><ul>
<li></li>
</ul></li>
</ul>


es sollte aber möglich sein deinen Ansatz entsprechend abzuändern - ich werds morgen mal versuchen...

Allerdings meint folgende Seite dass man auch irgendwas mit Tiefe, Nachfolger und Bruder konstruiren kann - es sollte als noch andere Ansätze geben.
http://ffm.junetz.de/members/reeg/dev/DSP/node97.html

ne0
2004-12-27, 20:13:46
nur 2 ebenen? nein, er machts halt (while) solang er noch results aus der db bekommt

das outgeputtete html sollte auch valid sein
so bei mir:

<ul>
<li>A - Das Wurzelposting</li>
<ul>
<li>B - Reply auf "A"</li>
<ul>
<li>D - Reply auf "B"</li>
<ul>
<li>F - Reply auf "D"</li>
</ul>
<li>G - Reply auf "B"</li>
</ul>
<li>C - Reply auf "A"</li>
<ul>
<li>E - Reply auf "C"</li>
</ul>
</ul>
</ul>


natürlich nicht so toll eingerückt :)

hier mein sql dump

CREATE TABLE `node2` (
`node_id` int(10) unsigned NOT NULL auto_increment,
`root_id` int(10) unsigned NOT NULL default '0',
`payload` varchar(64) default NULL,
`lft` int(10) unsigned NOT NULL default '0',
`rgt` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`node_id`),
KEY `root_id` (`root_id`)
) TYPE=MyISAM AUTO_INCREMENT=8 ;

--
-- Dumping data for table `node2`
--

INSERT INTO `node2` VALUES (1, 1, 'A - Das Wurzelposting', 1, 14);
INSERT INTO `node2` VALUES (2, 1, 'B - Reply auf "A"', 2, 9);
INSERT INTO `node2` VALUES (3, 1, 'C - Reply auf "A"', 10, 13);
INSERT INTO `node2` VALUES (4, 1, 'D - Reply auf "B"', 3, 6);
INSERT INTO `node2` VALUES (5, 1, 'E - Reply auf "C"', 11, 12);
INSERT INTO `node2` VALUES (6, 1, 'F - Reply auf "D"', 4, 5);
INSERT INTO `node2` VALUES (7, 1, 'G - Reply auf "B"', 7, 8);



ne0

MadMan2k
2004-12-27, 23:24:51
nur 2 ebenen? nein, er machts halt (while) solang er noch results aus der db bekommt
das schon, bloß fallen die ebenen nicht so linear wie sie steigen - sie werden aber nur einmal geschlossen.
Folgendes Beispiel sollte es verdeutlichen:

Level | Aktion
------+------
1 | <auf
2 | <auf
3 | <auf
1 | zu> // müsste aber 2x zu sein, da wir erst auf lvl2 sind



das outgeputtete html sollte auch valid sein
so bei mir:

das wäre nicht valide, da innerhalb des ul elementes nur li elemente erlaubt sind.
http://de.selfhtml.org/html/referenz/elemente.htm#ul

ne0
2004-12-28, 12:35:50
das schon, bloß fallen die ebenen nicht so linear wie sie steigen - sie werden aber nur einmal geschlossen.
Folgendes Beispiel sollte es verdeutlichen:

Level | Aktion
------+------
1 | <auf
2 | <auf
3 | <auf
1 | zu> // müsste aber 2x zu sein, da wir erst auf lvl2 sind



das wäre nicht valide, da innerhalb des ul elementes nur li elemente erlaubt sind.
http://de.selfhtml.org/html/referenz/elemente.htm#ul
ähm das ist doch ein neuer listenblock? du kannst ja auch zB ein span in ein li packen

zum 1. dafür ist ja die for schleife am ende da um am ende zu schliesen,

du musst doch damit rechnen das beim nested set nicht immer wieder auf die ursprungsebene zurückgekehrt wird

MadMan2k
2004-12-28, 13:11:34
ähm das ist doch ein neuer listenblock? du kannst ja auch zB ein span in ein li packen
Ich verstehe zwar nicht, was du mir damit sagen willst, daher wiederhole ich mich nochmal :)

richig wäre:

<ul>
<li>A
<ul> <!-- UL -> LI -> UL -->
<li>B - reply auf A</li>
</ul>
</li>
</ul>


falsch ist:


<ul>
<li>A</li>
<ul> <!-- UL -> UL -->
<li>B - reply auf A </li>
</ul>
</ul>

[/QUOTE]

zum 1. dafür ist ja die for schleife am ende da um am ende zu schliesen,

du musst doch damit rechnen das beim nested set nicht immer wieder auf die ursprungsebene zurückgekehrt wird[/QUOTE]
ahh.. deine Lösung ist gar nicht dafür gedacht Datensätze mit mehreren Ästen zu empfangen.

Ich will damit aber gezielt solche Strukturen erzeugen können:
http://www.php-resource.de/tutorials/images/nestedsets7.gif


+---------+----------+-------+
| payload | children | level |
+---------+----------+-------+
| A | 12 | 1 |
| B | 3 | 2 |
| C | 0 | 3 |
| D | 1 | 3 |
| E | 0 | 4 |
| F | 7 | 2 |
| G | 0 | 3 |
| H | 5 | 3 |
| I | 3 | 4 |
| J | 1 | 5 |
| K | 0 | 6 |
| L | 0 | 5 |
| M | 0 | 4 |
+---------+----------+-------+

ne0
2004-12-28, 13:40:48
hmm doch eigendlich schon, ich hab das selbe tutorial als grundlage gehabt

MadMan2k
2004-12-28, 14:07:43
deine Lösung mit dem Datensatz von oben:
http://img135.exs.cx/img135/4656/fehler7jo.png

MadMan2k
2004-12-28, 14:47:05
$lvl = 0;

foreach ($nestedSets as $set) {
if ($set[1] > $lvl) {
print "\n<ul>\n";
print "<li>".$set[0];
}
elseif ($set[1] < $lvl) {
print '</li>';
for($i = 0; $i < ($lvl - $set[1]); $i++){
print '</ul></li>'."\n";
}
print '<li>'.$set[0];
} else {
print '</li><li>'.$set[0];
}

$lvl = $set[1];
}

for($i = 0; $i < ($lvl - 1); $i++){
print '</ul></li>'."\n";
}

print '</ul>';


aber so recht sauber sieht das auch nicht aus - sonst noch Vorschläge?

ethrandil
2004-12-28, 15:02:54
Ja - machs rekursiv =)

MadMan2k
2004-12-28, 15:16:05
dann aber über die Anzahl der Kindelemente, so wie ich es mit parendID schon hatte.
Die Performance sollte aber besser sein, da ich nur noch einen Query an die DB brauche, oder?

ethrandil
2004-12-28, 16:21:55
Hmm, ja ich hab sowas immer mit einer ParentID realisiert...

Datenstruktur:
id, name, parentID

Pseudocode:

Funktion liste(Elemente, verwendete ParentID):
für jedes Element mit der verwendeten ParentID:
gib Elementstart aus (zb <ul>)
liste(Elemente, Id des aktuellen Elements)
gib Elementende aus (zb </ul>)
Funktion ende

Hauptfunktion:
Hole alle Elemente aus der Datenbank
liste( alle Elemente, 0 )


Wenn du Performancegeil bist, dann kannst du die Elemente beim select nach ParentID ordnen und dann die forschleife so anpassen, dass nur die notwendigen Elemente durchsucht werden... (schlecht ausgedrückt o.o)

- Eth

ethrandil
2004-12-28, 16:33:28
<?
function list($elements, $pid, $arrayStartIndex){
$ownID = $elements[$arrayStartIndex]['id'];

echo("<li>");//müsstest du dann so anpassen, dass es mal li, mal ul nimmt...
if($arrayStartIndex > 0)
echo($elements[$arrayStartIndex]['name']);

$nextIndex = $arrayStartIndex;
while($elements[$nextIndex]['parentID'] < $ownID)
$nextIndex++;

while($elements[$arrayStartIndex]['parentID'] == $pid){
list($elements, $ownID, $nextIndex);
}

echo("</li>");
}

$query =


ich editier gleich weiter, muss eben weg *g*

MadMan2k
2004-12-28, 19:52:13
Hmm, ja ich hab sowas immer mit einer ParentID realisiert...

Datenstruktur:
id, name, parentID

ja, ich kenn das ParentID System - allerdings musst du da die DB Rekursiv abfragen um einen Ast zu bekommen.

mit Nested Sets (http://ffm.junetz.de/members/reeg/dev/DSP/node92.html) kannst mit einem einzigen Query alle Vorgänger oder alle Nachfolger bekommen.

ethrandil
2004-12-28, 22:52:21
Nö, muss man garnicht =)

wenn man die Ergebnisse einfach nach ParentIDs sortiert, dann kann man das genauso effizient wie deine nested sets verwenden.

- Eth

MadMan2k
2004-12-28, 23:26:13
ja, wenn man den kompletten Baum haben will, aber normalerweise hat man bei Webpages nur eine id und muss schauen wie sie im Gesamtbaum drinhängt.

Bei Größenordungen dieses Forums kann man dann nicht mehr den kompletten Baum laden... ;)

aber um auf mein Problem zurückzukommen:

eigentlich habe ich schon ne Funktion die mir das ganze rekursiv auflöst - diese braucht allerdings ein Objekt, welches wie in Java das DefaultMutableTreeNode aufgebaut ist.
Im Prinzip geht es also nur darum sowas rekursiv zu erstellen - aber irgendiwe hören alle Tutorials an dieser Stelle auf :(

das oben verlinkte gibt nur den Tipp:

Um einen Baum sauber in HTML ausgeben zu können, braucht man folgende Informationen: Name des Elements, Anzahl der Nachfolger (bzw. gibt es einen oder keinen), die Tiefe des Elements und ob es auf gleicher Tiefe noch ein Element gibt.

MadMan2k
2004-12-29, 14:03:08
finally...


function makeNode($array) {

$node = new TreeNode($array);

for($i = 0; $i < $array['children]; $i++){
$node->add(makeNode(mysql_fetch_array($query)));
}

return $node;
}


eigentlich gar nicht so schwer, wenn man sich erstmal von der while Schleife verabschiedet hat...