[PHP] Recursief menu

Pagina: 1
Acties:
  • 175 views sinds 30-01-2008
  • Reageer

Onderwerpen


Acties:
  • 0 Henk 'm!

  • RRX
  • Registratie: Mei 2000
  • Laatst online: 29-05 15:34

RRX

@life-

Topicstarter
Ik ben bezig met het maken van een recursief menu. Ik ben behoorlijk ver gekomen, maw ik krijg het gewenste menu in beeld, maar een optie die ik nog in het menu wil hebbben krijg ik er niet in;

Ik krijg nu als output:
code:
1
2
3
4
5
<div id='menuItem1' class='menuItemLevel0'><a href='./?id=1' title='Dit is de homepagina' name='Dit is de homepagina'>Home</a></div>
<div id='menuItem2' class='menuItemLevel1'><a href='./?id=2' title='Hier kunt u de informatie over het systeem bekijken' name='Hier kunt u de informatie over het systeem bekijken'>Systeeminformatie</a></div>
<div id='menuItem3' class='menuItemLevel2'><a href='./?id=3' title='Wheeeeee' name='Wheeeeee'>Details</a></div>
<div id='menuItem4' class='menuItemLevel0'><a href='./?id=4' title='Contactinformatie' name='Contactinformatie'>Contact</a></div>
<div id='menuItem5' class='menuItemLevel1'><a href='./?id=5' title='Neem direct contact met ons op!' name='Neem direct contact met ons op!'>Informatieaanvraag</a></div>


maw: het id van iedere div is het menuItem<uniekID> om ieder menuitem afzonderlijk te kunnen aansturen. De class van iedere div is 'menuItemLevel0' 'menuItemLevel1' etc. etc om voor ieder niveau een andere opmaak aan te kunnen geven.

De tabelstructuur die ik gebruik is de volgende:
code:
1
2
3
4
5
6
id  parent_id   page_id name            description     created  
1   0       1   Home            Dit is de homepag...    2007-07-09
2   1       3   Systeeminformatie   Hier kunt u de in...    2007-07-09 
3   2       2   Details         Wheeeeee        2007-07-09
4   0       3   Contact         Contactinformatie   2007-07-09
5   4       4   Informatieaanvraag  Neem direct conta...    2007-07-09

Voor ieder menuitem geef ik aan of het menuitem een parent heeft. maw. Wanneer een menuitem een child is, heeft hij in parent_id het id van zijn parent staan.

Nu heb ik hieruit de volgende array kunnen extraheren:
code:
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
Array
(
    [0] => Array
        (
            [id] => 1
            [childs] => Array
                (
                    [0] => Array
                        (
                            [id] => 2
                            [childs] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 3
                                            [childs] => 
                                            [parent_id] => 2
                                            [page_id] => 2
                                            [name] => Details
                                            [description] => Wheeeeee
                                        )

                                )

                            [parent_id] => 1
                            [page_id] => 3
                            [name] => Systeeminformatie
                            [description] => Hier kunt u de informatie over het systeem bekijken
                        )

                )

            [parent_id] => 0
            [page_id] => 1
            [name] => Home
            [description] => Dit is de homepagina
        )

    [1] => Array
        (
            [id] => 4
            [childs] => Array
                (
                    [0] => Array
                        (
                            [id] => 5
                            [childs] => 
                            [parent_id] => 4
                            [page_id] => 4
                            [name] => Informatieaanvraag
                            [description] => Neem direct contact met ons op!
                        )

                )

            [parent_id] => 0
            [page_id] => 3
            [name] => Contact
            [description] => Contactinformatie
        )

)


Iedere array bevat;
[0] => Array
(
[id] => id van menuiten
[childs] => Eventuele nieuwe array met gegevens voor child
[parent_id] => ID van parent
[page_id] => 2
[name] => Naam van het menu
[description] => Omschrijving van het menu
)

Nou heb ik in mijn html template een menu-class geschreven;

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
class html
{
    function menu($array,$level=1,$currentid=0)
    {
        global $menuOutput;
        $levelCount = $level;
        foreach($array as $menuitems)
        {
                #parent menu-items
                $menuOutput = $menuOutput."<div id='menuItem" . $menuitems[id] . "' class='menuItemLevel" . $level . "'><a href='./?id=" . $menuitems[id] . "' title='" . $menuitems[description] . "' name='" . $menuitems[description] . "'>" . $menuitems[name] . "</a></div>\n";

                    $levelCount++;  
                    #recursive child menu-items should only show when parent is in row
                    if(is_array($menuitems[childs]))
                    {                           
                        $this->menu($menuitems[childs],$levelCount,$currentid);
                    }

                #clear $levelCount after use so next loop starts at 0 again.
                unset($levelCount);
            
        }

        return $menuOutput;
        
    }
}


die roep ik op de volgende manier aan;

PHP:
1
2
3
4
$frontpage = new html();

$menucontent = $frontpage->menu($menuArrayResult,0,$_GET[id]);
        echo $frontpage->layer("mainMenu",$menucontent);


Dan krijg ik dus het menu zoals ik dat wil, zie het eerste stukje html

Nu komt het probleem;

Ik krijg nu het hele menu, met alle items, terwijl ik de optie wil hebben om alleen wanneer ik een parent heb aangeklikt, de childs van die specifieke parent weer te geven, de andere parents die hun parent_id op 0 hebben moeten uiteraard wel allemaal weergegeven worden.

Ik krijg het niet voor elkaar om uit de recursieve array te breken...

mijn T.net systeemspecspagina


Acties:
  • 0 Henk 'm!

  • Kalentum
  • Registratie: Juni 2004
  • Nu online
Op de plek waar je kijkt of een item kindjes heeft kun je ook een check uitvoeren op het id van het item zelf.

iets als:
Regel 14:
PHP:
1
if(is_array($menuitems[childs]) && $menuitems['id'] == $currentid)


En nog een check op level

[ Voor 8% gewijzigd door Kalentum op 30-07-2007 12:24 ]


Acties:
  • 0 Henk 'm!

  • RRX
  • Registratie: Mei 2000
  • Laatst online: 29-05 15:34

RRX

@life-

Topicstarter
rutgerw schreef op maandag 30 juli 2007 @ 12:23:
Op de plek waar je kijkt of een item kindjes heeft kun je ook een check uitvoeren op het id van het item zelf.

iets als:
Regel 14:
PHP:
1
if(is_array($menuitems[childs]) && $menuitems['id'] == $currentid)


En nog een check op level
Dat had ik ook al bedacht,
rutgerw schreef op maandag 30 juli 2007 @ 12:23:
Op de plek waar je kijkt of een item kindjes heeft kun je ook een check uitvoeren op het id van het item zelf.

iets als:
Regel 14:
PHP:
1
if(is_array($menuitems[childs]) && $menuitems['id'] == $currentid)


En nog een check op level
Dat had ik ook al bedacht, máár dat gaat alleen goed op het moment dat je de parent zonder parent_id hebt geselecteerd.

Zonder check krijg ik alles:
Home
-Systeeminformatie
--Details
Contact
-Informatieaanvraag

Zodra ik die check toevoeg en ik selecteer Home krijg ik:
Home
-Systeeminformatie
Contact

Nou is het dus de bedoeling als ik op Systeeminformatie klik, dat ik dan ;
Home
-Systeeminformatie
--Details
Contact
Te zien krijg

Maar dat gebeurt niet omdat $menuitems[id] bij de controle de id van de parent is, en die komt dus niet overeen met de de huidige id (in dit geval 2) de parent heeft id 1.

mijn T.net systeemspecspagina


Acties:
  • 0 Henk 'm!

  • BikkelZ
  • Registratie: Januari 2000
  • Laatst online: 21-02 08:50

BikkelZ

CMD+Z

Dus kort samengevat je hebt een menuboomstructuur die oneindig diep kan zijn, en je wil dat waar die persoon geklikt heeft in de boomstructuur dat dat tot daar zichtbaar wordt plus de eerste rij?

Ik denk zoiets:

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
function parseMenu()
{
$parseMe = false;
if ($this->getID() !== CLICKED_ID && $this->getParent() !== null)
{
if ($this->countNodes() > 0)
{
foreach($this->childNodes as $childNode)
{
$parseMe = $childNode->parseMenu();
if ($parseMe) break;
}
}
}
else
{
$parseMe = true;
}
if ($parseMe) parseChildNodes();
return $parseMe;
}

function parseChildNodes()
{
foreach ($this->childNodes as $childNode)
{
$childNode->parseNode();
}
}


Dus als een treenode geen parent heeft (dus een van de roottreenodes is), dan parst hij, en als hij de aangeklikte is of een van zijn childnodes of daar weer een child van aangeklikt is. Als aan een van je childnodes geklikt is mag je ook uit de loop breaken.

Door te returnen of je wel of niet parst aan je parentnode als return op de recursieve functie weet die ook gelijk of die moet parsen of niet.

En het spijt me voor de tabs, die krijg ik er op deze manier niet in.

------------------------

Hmm het is iets ingewikkelder dan dat natuurlijk. Je wilt de siblings laten parsen, en je wil ook de eerste laag aan childNodes parsen. Misschien een parseChildnodes() functie bouwen die aangeroepen wordt als $parseMe true is?

------------------------

Ach er zitten nog wat foutjes in maar het idee is duidelijk.

[ Voor 20% gewijzigd door BikkelZ op 30-07-2007 13:46 ]

iOS developer


Acties:
  • 0 Henk 'm!

  • SWINX
  • Registratie: Juni 2001
  • Laatst online: 23-07 18:19
Ik zat zelf ook altijd te "kloten" met dit soort situaties en ik heb daarom gekozen voor deze constructie

10 Home
1010 Systeeminformatie
101010 Details
1011 ....
1012 ....
101210 subitem van 1012
11 Contact
1110 Informatieaanvraag

Als je bijvoorbeeld op 1010 zou zijn kun je dus afvragen dat 101010 hier nog onderligt en je weet meteen dat 10 je parent is

Ik vind het persoonlijk fijner werken dan het parent gebeuren, misschien heb je er wat aan :).

Mannen komen van Mars Tweakers, vrouwen van Venus Bokt


Acties:
  • 0 Henk 'm!

  • kokx
  • Registratie: Augustus 2006
  • Laatst online: 13-09 20:30

kokx

WIN

Voor deze vorm van recursie gebruik ik altijd een vorm van dit om door de array te loopen:

PHP:
1
2
3
4
5
6
7
8
9
10
11
<?php
array_map('recursion', $menu);

function recursion($var)
{
    if (is_array($var)) {
        array_map('recursion', $var);
    } else {
        // doe iets met $var
    }
}

Acties:
  • 0 Henk 'm!

  • RRX
  • Registratie: Mei 2000
  • Laatst online: 29-05 15:34

RRX

@life-

Topicstarter
Bedankt voor alle tips;

SWINX > Iets dergelijks als jou manier gebruikte ik tot voorkort ook, maar ik wil graag op een wat nettere manier te werk.

Ik heb nu al iets gerealiseerd dat goed werkt, ik ben alleen nog op zoek naar 1 optimalisatie;

Ik heb een array uit de andere array geextract die er als volgt uitziet:
code:
1
2
3
4
5
6
7
8
9
10
$idParent =

Array 
( 
    [1] => 0 
    [2] => 1
    [3] => 2
    [4] => 0 
    [5] => 4
)


Vervolgens doe ik trapsgewijs een check of de huidige id voor komt in een van bovende parents. Nadeel is wel, ik kan nu zo diep gaan met mijn menu, als dat ik hier een extra or toevoeg. Dus dit stukje moet ik nog optimaliseren.
PHP:
1
2
3
4
5
6
7
8
if(
    ($idParent[$idParent[$currentid]] == $menuitems[id]) 
    OR ($idParent[$idParent[$idParent[$currentid]]] == $menuitems[id])
    OR ($idParent[$idParent[$idParent[$idParent[$currentid]]]] == $menuitems[id])
)
{
    //recursief functie uitvoeren
}

mijn T.net systeemspecspagina


Acties:
  • 0 Henk 'm!

  • BikkelZ
  • Registratie: Januari 2000
  • Laatst online: 21-02 08:50

BikkelZ

CMD+Z

RRX schreef op woensdag 01 augustus 2007 @ 12:07:
Bedankt voor alle tips;

SWINX > Iets dergelijks als jou manier gebruikte ik tot voorkort ook, maar ik wil graag op een wat nettere manier te werk.

Ik heb nu al iets gerealiseerd dat goed werkt, ik ben alleen nog op zoek naar 1 optimalisatie;

Ik heb een array uit de andere array geextract die er als volgt uitziet:
code:
1
2
3
4
5
6
7
8
9
10
$idParent =

Array 
( 
    [1] => 0 
    [2] => 1
    [3] => 2
    [4] => 0 
    [5] => 4
)


Vervolgens doe ik trapsgewijs een check of de huidige id voor komt in een van bovende parents. Nadeel is wel, ik kan nu zo diep gaan met mijn menu, als dat ik hier een extra or toevoeg. Dus dit stukje moet ik nog optimaliseren.
PHP:
1
2
3
4
5
6
7
8
if(
    ($idParent[$idParent[$currentid]] == $menuitems[id]) 
    OR ($idParent[$idParent[$idParent[$currentid]]] == $menuitems[id])
    OR ($idParent[$idParent[$idParent[$idParent[$currentid]]]] == $menuitems[id])
)
{
    //recursief functie uitvoeren
}
Als je die check ook niet uitvoert terwijl je recursief alle nodes na aan het lopen bent blijft het ook aanmodderen. Je loopt gewoon recursief eerst je tree af, bij een match ga je over tot parseChildNodes waarmee je recursief naar boven steeds de parentnode aanroept om de childnodes te gaan renderen en de childnodes om ook weer de childnodes te gaan renderen tot er geen parentnodes en childnodes meer zijn in dat stuk van je tree, en break je je recursieve tree traversal.

Let op dat je moet voorkomen dat je via je parentnode weer je clickednode aanroept om te gaan renderen, anders kom je in een lock terecht.

iOS developer


Acties:
  • 0 Henk 'm!

  • Kalentum
  • Registratie: Juni 2004
  • Nu online
Misschien dat dat gedoe met parent_id's het ook niet helemaal is. Zie http://www.sitepoint.com/article/hierarchical-data-database

Acties:
  • 0 Henk 'm!

  • BikkelZ
  • Registratie: Januari 2000
  • Laatst online: 21-02 08:50

BikkelZ

CMD+Z

+1

Maximale flexibiliteit!

iOS developer


Acties:
  • 0 Henk 'm!

  • RRX
  • Registratie: Mei 2000
  • Laatst online: 29-05 15:34

RRX

@life-

Topicstarter
Ik heb mijn database dus precies hetzelfde, alleen bij hun is de naam het unieke id en ik gebruik cijfers als id.
edit:

had pagina 2 en 3 niet gelezen, dat is inderdaad anders, maar niet handig voor mij.


Het linkje is wel erg +1 :) handig!

Het nadeel van pagina 1 was dat er veel databasequeries voor nodig zijn, daarom heb ik eerst mijn hele menu in een array geladen.

[ Voor 27% gewijzigd door RRX op 01-08-2007 15:14 ]

mijn T.net systeemspecspagina


Acties:
  • 0 Henk 'm!

  • RRX
  • Registratie: Mei 2000
  • Laatst online: 29-05 15:34

RRX

@life-

Topicstarter
BikkelZ schreef op woensdag 01 augustus 2007 @ 13:04:
[...]


Als je die check ook niet uitvoert terwijl je recursief alle nodes na aan het lopen bent blijft het ook aanmodderen. Je loopt gewoon recursief eerst je tree af, bij een match ga je over tot parseChildNodes waarmee je recursief naar boven steeds de parentnode aanroept om de childnodes te gaan renderen en de childnodes om ook weer de childnodes te gaan renderen tot er geen parentnodes en childnodes meer zijn in dat stuk van je tree, en break je je recursieve tree traversal.

Let op dat je moet voorkomen dat je via je parentnode weer je clickednode aanroept om te gaan renderen, anders kom je in een lock terecht.
Deze check doe ik dus ook tijdens het langslopen van mijn nodes. en ik roep dezelfde functie dus weer aan bij true.

mijn T.net systeemspecspagina


Acties:
  • 0 Henk 'm!

  • BikkelZ
  • Registratie: Januari 2000
  • Laatst online: 21-02 08:50

BikkelZ

CMD+Z

Ja, maar als je gewoon op het moment dat blijkt dat er op die node geklikt is wederom een recursieve functie aan het werk zet, dan kun je die boom zo diep maken als je zelf wilt.

iOS developer

Pagina: 1