[PHP] Problemen met recursieve functie voor navigatie output

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
ik heb de volgende tabel (alleen relevante data):
code:
1
2
3
4
5
6
7
8
9
10
+---------+----------+-----------+
| node_id | node_pid | node_name |
+---------+----------+-----------+
| 3       | 55       | contact   |
| 11      | 55       | over ons  |
| 54      | 55       | diensten  |
| 55      | 0        | home      |
| 56      | 54       | dienst A  |
| 58      | 54       | dienst B  |
+---------+----------+-----------+

Het idee is dat ik via de querystring een id aanroep, waarna de navigatie - een uitgeklapt menu - gebouwd wordt door een recursieve functie. Ga uit van index.php?id=56, dan moet de output zijn:

HTML:
1
2
3
4
5
6
home
   contact
   over ons
   diensten
      dienst A (vet gedrukt)
      dienst B

Tweede voorbeeld: index.php?id=11 zou als output moeten geven:

HTML:
1
2
3
4
home
   contact
   over ons (vet gedrukt)
   diensten

In mijn pogingen dit te bereiken heb ik nu het volgende:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$tree = '';
$indent = '';

function get_tree($id) {
    global $tree, $indent;

    $res_parent = db_query('SELECT node_pid, node_name FROM nodes WHERE node_id = '.$id, 1, 0);
    $row_parent = db_fetch_array($res_parent);

    $res_bros = db_query('SELECT node_id, node_name FROM nodes WHERE node_pid =
                         '.$row_parent['node_pid'], 1, 0);
    $num_bros = db_num_rows($res_bros);
    
    for ($i = 0; $i < $num_bros; $i++) {
        $row_bros = db_fetch_array($res_bros);
        $tree .= $indent.$row_bros['node_name'].'<br>';
    }
    
    $indent .= '&nbsp;&nbsp;&nbsp;';
    
    if($row_parent['node_pid'] != 0) {
        $tree .= get_tree($row_parent['node_pid']);
    }
}

get_tree($_GET['id']);

Maar in het eerste voorbeeld genereert deze de volgende output:

HTML:
1
2
3
4
5
6
dienst A
dienst B
   contact
   over ons
   diensten
      home

Je ziet - ik zit met twee problemen:
• het menu zit in omgekeerde volgorde
• de output wordt 'laag voor laag' geparsed - in plaats dat dienst A en dienst B onder 'over ons' staan, staan zij onder (of eigenlijk: boven) contact

Ik zit hier nu al een aantal avonden op te stoeien, maar kom niet verder. Kan iemand mij op weg helpen?

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

Waarom bouw je niet een array op en draai je het dan gewoon om? array_reverse()

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

Verwijderd

Je begint nu ook met zoeken onder aan de tree, wat je ook zou kunnen doen is boven aan beginnen (node_id =0) en de daarbij behorende childeren op te zoeken. Automagish, komt dan alles in de goeie volgorde.

Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Laatst online: 16:36
Ik gebruik zelf de volgende functie om een sitemap te genereren. Misschien heb je er wat aan:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function maketree($startnode, $firstcall)
{
global $db;

if ($firstcall == 1) { $output = "<dl id=\"tree\">\n"; } else { $output = ''; }

$namequery = mysql_query("SELECT name FROM tree WHERE page_id = '" .$startnode ."' LIMIT 1", $db);
    if ($name = mysql_fetch_assoc($namequery)) 
        {
        $output .= "  <dt>" .$name["name"] ."</dt>\n";
        }

$childquery = mysql_query("SELECT page_id FROM tree WHERE parent_id = '" .$startnode ."' ORDER BY page_id ASC", $db);
  if ($children = mysql_fetch_assoc($childquery))
    {
      $output .= "  <dd>\n  <dl>\n";
      do {
            $output .= maketree($children["page_id"], 0);
         } while ($children = mysql_fetch_assoc($childquery));
      
      $output .="  </dl>\n  </dd>\n";
    }

if ($firstcall == 1) { $output .= "</dl>\n"; }
return $output;
}

Aanroepen met maketree(*hoogste node id*, 1).

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
@Dutch_Wolf - beginnen met de hoogste node heeft twee nadelen:

• ik moet niet de hele tree ophalen; slechts relevante nodes. Als ik bovenaan begin is het niet te bepalen welke nodes ik wel en niet op moet halen
• met de nodige checks zou het wel kunnen eventueel, maar dit zorgt voor onnodig extra queries

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


Acties:
  • 0 Henk 'm!

Verwijderd

Volgens mij niet begin dan gewoon met de hoogste node die je nodig hebt, als je onder aan begint haal je ze allemaal op, als je bovenaan begint, haal je alleen op wat je nodig hebt. (je kan dus ook op node_id 4 beginnen ofzo)

Het lijkt mij juist dat als je onderaan begint je meer moet doen.

Acties:
  • 0 Henk 'm!

  • ikke007
  • Registratie: Juni 2001
  • Laatst online: 18-09 14:10
Ik heb de volgende klasse geschreven om een menu te genereren, Aan de hand van de ID welke geselecteerd is loopt hij eerst door de tabel heen tot hij alle childs in het pad tot de geselecteerde id heeft gevonden, daarna gaat hij het menu opbouwen en uiteindelijk gaat hij indien dat aanwezig is het submenu van de geselecteerde ID weergeven (om indien nodig verder te kunnen klikken)..

Er zit wel wat maatwerk in maar kan met een kleine aanpassing hier en daar goed werken voor jou.

Als je het gebruikt --> Stuur me even een mailtje. Ik vindt het wel zo leuk om te zien waar mijn code heen gaat ;)

Altijd handig om te weten:
• $g = selected node
• node_id = id
• node_parent = parent id
• node_name = naam van de menu item
• menu = tabel naam
• node_position = volgorde van links in tabel (als je iets met een Z bovenaan wilt)
• bestand = de url naar de pagina als je op de menuitem klikt (index.php oid)

Uhm... de Layout is wel grondig vern**kt.. excuse me

screenshot hoe het eruitziet:(met wat css erbij)
Afbeeldingslocatie: http://www.de-ftt.nl/images/menu.jpg


PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?PHP
IF(ISSET($_GET['g'])){
    $g          = $_GET['g'];
    $menu       = new menu($g);
}
else{
    #build the menu :)
    $menu       = new menu();
}

$menu->getMenu();

#the thing that does it all! --VV-- follows next 
class menu{
    #Menu building class
    #copyright Mark van Straten - 2004 - markATde-fttDOTnl (AT = @ DOT = .)
    #If you want to use this code please contact me
    #declare all vars
    var $parent;#parent of the menu
    var $child;#child (selected item) of the menu

    var $path; #array with id's of path
    var $menu = "<!--   #copyright Mark van Straten -(c)2004 - markATde-fttDOTnl (AT = @ DOT = .)                       #If you want to make use this code please contact me
                -->"
    ; #the HTML menu items
    
    var $page; #var met daarin de url naar de geselecteerde pagina

    function menu($selectedItem=1){
        #constructor
        connectDatabase();#connect to the appropiate db

        #Create Path from root till selected child
        $this->selectedItem($selectedItem);#find $path & page wich to show
        #give HTML output of menu with included submenu (if selected)       
        $this->parent   = 0;
        $this->child    = $selectedItem;
        
        $this->setSubmenu(1,$this->child);#build menu $menu
    }

    #function to find the selected (root) menuitem wich belongs to subitem $id  
    function selectedItem($id=1){
        $sql_find = "
            SELECT node_id,node_parent,bestand
            FROM menu
            WHERE node_id = '".$id."'
        ";
        $exc_find = mysql_query($sql_find) or die();
        if(mysql_num_rows($exc_find) <> 0){
            #er zijn resultaten
            $rst_find = mysql_fetch_array($exc_find,MYSQL_ASSOC);
            if($this->page == '' && !EMPTY($rst_find['bestand'])){
                $this->page = $rst_find['bestand'];
            }
            if($rst_find['node_parent'] > 1){
                $this->path[] = $rst_find['node_id'];
                $this->selectedItem($rst_find['node_parent']);
            }
            else{
                #we found the last one of them!
                $this->path[] = $rst_find['node_id'];
                $this->path[] = $rst_find['node_parent'];
                return;
            }
        }
    }
    
    #function to create subitems till we found selectedsubitems from    
    function setSubmenu($parent=0,$child=1,$parentUrl='index.php',$level=0){
        #als de meegekregen parent de gezochte child is ==> einde!
        #deze rij ophalen om te printen
        if($parent == $child){
            #ze zijn gelijk.. wat doen we ermee?
        }
        
        $sql_menu = "
            SELECT *
            FROM menu
            WHERE node_parent = '".$parent."'
            ORDER BY node_position
        ";
        $exc_menu = mysql_query($sql_menu) or die();
        if(mysql_num_rows($exc_menu) == 0){
            #ging niet goed of geen results --> return!
            return;
        }
        else{
            #deze 'menuitem' opslaan en indien nodig dieper gaan in de structuur
            $style = '';
            if($parent <= 1){
                $style = 'menu';
            }
            $this->menu .= '<ul class="'.$style.'" id="'.$style.'">';
            while($rst_menu = mysql_fetch_array($exc_menu,MYSQL_ASSOC)){
                #als de parent in het pad ligt dan moet deze wit zijn :)
                $class = ' onMouseOver="this.className=\'white\';" onMouseOut="this.className=\'\';"';              
                if(is_array($this->path) && (in_array($rst_menu['node_id'],$this->path))){
                    $class = ' class="white" ';         
                }
                if(!EMPTY($rst_menu['bestand'])){
                    $parentUrl = $rst_menu['bestand'];
                }
                $this->menu .= '<li '.$class.' style="cursor:pointer;" onClick=""><A class="bla" href="'.$parentUrl.'?g='.$rst_menu['node_id'].'" ALT="klap menu uit">'.$rst_menu['node_name'].'</li></A>';
                if(is_array($this->path) && (in_array($rst_menu['node_id'],$this->path))){
                    #het is in het pad we gaan dus dieper indien dat kan
                    $this->setSubmenu($rst_menu['node_id'],$child,$parentUrl,$level+1);
                }
                else{
                    #het id ligt niet in het pad, dus we doen er lekker niets mee!@
                }
            }
            $this->menu .="</ul>";
            return;
        }
    }

    #return the menu
    function getMenu(){
        print($this->menu);
    }
    #return the URL to the highlighted page
    function getPage(){
        return $this->page;
    }
}
?>

[ Voor 32% gewijzigd door ikke007 op 25-05-2004 10:28 . Reden: Plaatje toegevoegd ]

Lets remove all security labels and let the problem of stupidity solve itself


Acties:
  • 0 Henk 'm!

  • Skaah
  • Registratie: Juni 2001
  • Laatst online: 16-09 18:38
Begin van onder.
code:
1
2
3
4
5
6
7
8
9
10
+---------+----------+-----------+
| node_id | node_pid | node_name |
+---------+----------+-----------+
| 3       | 55       | contact   |
| 11      | 55       | over ons  |
| 54      | 55       | diensten  |
| 55      | 0        | home      |
| 56      | 54       | dienst A  |
| 58      | 54       | dienst B  |
+---------+----------+-----------+

Stel dat je dienst B wilt tonen. Zoek eerst op welke je dan allemaal moet openklappen. Haal telkens het parentId op, totdat je bij 0 bent. De node_id's bewaar je in een array.

Daarna kun je op de manier waarop je normaal ene lijst maakt, dus top-down, alleen de niveau's opzoeken die in je array staan. (55, 54, 58).

Voorbeeldje: www.granaet.nl (linkermenu). Code is eventueel wel beschikbaar. (mail)

Acties:
  • 0 Henk 'm!

  • ikke007
  • Registratie: Juni 2001
  • Laatst online: 18-09 14:10
Regel 43 van mijn code begint mijn functie selectedItem welke het pad bepaald van de geselecteerde items. Als de Ts die functie gebruikt is hij al een heel eind voor zijn probleem

Lets remove all security labels and let the problem of stupidity solve itself


Acties:
  • 0 Henk 'm!

  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
@iKKe007 - dank je wel zeg! Ik heb eea aangepast aan mijn eigen situatie, maar jouw code heeft me echt uit de brand geholpen :) Als alles helemaal werkt, stuur ik je zeker een mailtje met de volledige code zoals ik hem heb.

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."

Pagina: 1