PDA

Volledige versie bekijken : [php] unlimited levels menu structuur..


mech7
%Europe/Berlin %975 %2006, 23:24
Weet iemand een goede manier om een menu met oneindige menu structuur te laten zien. Ik heb een column als volgt.


id | parent_id | name
1 animals
2 1 cows
3 1 birds
4 3 eagles
5 3 sparrows


Het enigste wat ik nu kan bedenken is alle gegevens op halen. Loopen bij elk item kijken of het id hetzelfde als parentid op de items. Als dat zo is dan weer voor elk van die items het eerste herhalen...

Dit moet toch beter kunnen aangezien je het hele menu al ophaalt met 1 query. Heeft iemand suggesties, ideeen, tuts?

Ik heb een voorbeeld in een boek gevonden alleen dat ding is zo huge en complex dat ik denk dat er toch een makkelijkere manier voor moet zijn 8~

Macs
%Europe/Berlin %384 %2006, 09:14
kun je niet met een xml bestand werken?

brossiekoppie
%Europe/Berlin %436 %2006, 10:28
En hoe zou je dat dan willen weergeven?

mech7
%Europe/Berlin %516 %2006, 12:24
Ja xml bestand is ook mogelijk.. maar ik heb nog niet zoveel met die php5 xml functies gewerkt.
Hoe zou ik daar dan alle childs van de parents kunnen weergeven ?

Het weergeven wordt gewoon iets van:


<ul>
<li>Parent 1
<ul>
<li>Item 2</</li>
</ul>
</li>
</ul>

Macs
%Europe/Berlin %539 %2006, 12:56
ok, het was me in 1e instantie niet helemaal duidelijk wat je bedoelde, maar als ik het nu allemaal lees wil je dit:
- je hebt een database met daarin 3 columns;
> id
> parent.id
> naam
- de records in je datbase zijn niet geordend, maar je wilt ze wel geordend weergeven alle items gerangschikt onder hun eigen parent id.
- dit kan natuurlijk op verschillende manieren, maar hoe ik het idd doen mbv xml
- laat de php voor elk record de hierarchie (wat is de parent_id?>wat is de parent_id daarvan?>etc)bepalen en daarmee op de juiste plaats in de xml plaatsen; mbv DOM XML functions (werking is in principe hetzelfde als van de XML class in flash)
http://www.php.net/manual/nl/ref.domxml.php
- vervolgens laat je het xml document door bijv. de XML Parser Functies omzetten in html....


enige voorwaarde is dat in de database een een parent al bestaat al een child wordt gelezen, anders wordt het idd wel heel moeilijk....

dioneo
%Europe/Berlin %574 %2006, 13:47
Kijk hier eens naar.



function recursivePage($pid,$depth){
global $depths,$plus,$result;
$first=1;
$query="select id, pagina_id, menunaam, omschrijving from pagina where pagina_id ".($pid==0?'is null':'='.$pid)." order by volgorde asc";
$res = mysql_query($query) or die (mysql_error());
$last = mysql_num_rows($res);
while ($record = mysql_fetch_object($res)) {
$plus=0;
$result.= '<tr valign="top" bgcolor="'.(is_int($depth/2)?'#eeeeee':'white').'" ><td>';
$result.= '<img src="/editimages/blank.gif" />';
$result.= '<img src="/editimages/blank.gif" />';
switch ($first){
case 1;
$img='first.gif';
$depths[$depth]=1;
break;
case $last:
$img='last.gif';
$depths[$depth]=0;
break;
default:
$img='mid.gif';
$depths[$depth]=1;
break;
}
if($last==1){
$img='single.gif';
$depths[$depth]=0;

}
for($i=0;$i<$depth;$i++){
$result.= '<img src="/editimages/'.($depths[$i]==1?'follow':'blank').'.gif" />';
}
$result.= '<img src="/editimages/'.$img.'" />&nbsp;';
$result.= str_replace(' ','&nbsp;',$record->menunaam);
$result.= '&nbsp;&nbsp;&nbsp;</td><td align="right">';
$result.= '</td></tr>';
$first++;
$result.=recursivePage($record->id,($depth+1));
}
}
$result='';
$depths=Array();
recursivePage(0,0);
echo $result;


Maakt een recursief menu op basis van die structuur. Er wordt een boomstructuur gemaakt op die manier. Bijgevoegde plaatjes zijn als volgpad bedoeld.

Ik heb een voorbeeld in een boek gevonden alleen dat ding is zo huge en complex dat ik denk dat er toch een makkelijkere manier voor moet zijn
dat dacht ik ook :)

mech7
%Europe/Berlin %634 %2006, 15:13
Is er een functie om meteen door alle ouders en kinderen heen te loopen? En is hoe gemakkelijk is het om het hoogste id te retrieven? Moet de boel ook gecached worden om ervoor te zorgen dathet systeem niet elke keer de xml file in zijn geheugen laad met elke page request ?

ok, het was me in 1e instantie niet helemaal duidelijk wat je bedoelde, maar als ik het nu allemaal lees wil je dit:
- je hebt een database met daarin 3 columns;
> id
> parent.id
> naam
- de records in je datbase zijn niet geordend, maar je wilt ze wel geordend weergeven alle items gerangschikt onder hun eigen parent id.
- dit kan natuurlijk op verschillende manieren, maar hoe ik het idd doen mbv xml
- laat de php voor elk record de hierarchie (wat is de parent_id?>wat is de parent_id daarvan?>etc)bepalen en daarmee op de juiste plaats in de xml plaatsen; mbv DOM XML functions (werking is in principe hetzelfde als van de XML class in flash)
http://www.php.net/manual/nl/ref.domxml.php
- vervolgens laat je het xml document door bijv. de XML Parser Functies omzetten in html....


enige voorwaarde is dat in de database een een parent al bestaat al een child wordt gelezen, anders wordt het idd wel heel moeilijk....

meagain
%Europe/Berlin %815 %2006, 19:34
Dit is best moglijk, maar dat kan je met één query niet oplossen. Je zal eerst het begin ('wortel') moeten ophalen met een query, en dan moeten 'afdalen' in de boomstructuur door telkens opnieuw te SELECT-eren.

Daar is eigenlijk betrekkelijk weinig code voor nodig; je functie moet recursief werken....

dioneo
%Europe/Berlin %860 %2006, 20:39
deze query kun je zelf dynamisch opbouwen, waarbij de gekleurde regels [door php] herhaald kunnen worden. je snapt dat p1 dan p2 wordt. zo kun je in 1 query toch x lagen diep ineens ophalen.


select p0.id
, p1.id as id1
, p0.menunaam
, p1.menunaam as menunaam1
from pagina p0
, pagina p1
where
p1.pagina_id=p0.id and
p0.pagina_id is null
order by p0.volgorde asc
, p1.volgorde asc

je gebruikt vervolgens php om per regel te kijken of id1 gelijk is aan id1 van de vorige regel.


als je m helemaal uitbouwt met

case when p3.id is null then p2.id else
case when p2.id is null then p1.id else
case when p1.id is null then p0.id
end
end
end
as id
kun je alles rechttoerechtaan ophalen, maar dan moet je left outer joins maken van p1,p2 en p3 en weet je de diepte van je menulaag niet.

meer wegen naar rome, recursief het beste.

De Kale
%Europe/Berlin %498 %2006, 11:57
dit kun je oplossen met een zogeheten 'self join' waarbij je twee aliassen voor dezelfde tabel aanmaakt, zoals dineo al liet zien.
recursie is idd de way to go...

dit zou je ook in je stored procedures in je database kunnen uitvoeren ipv in php

mech7
%Europe/Berlin %665 %2006, 15:58
Met store procedure bedoel je daar zon left en right collumn?
http://dev.mysql.com/tech-resources/articles/hierarchical-data.html

Wnat dat lijkt me erg onhandig met opslaan als de childeren groter worden.

mech7
%Europe/Berlin %539 %2006, 12:56
:) ik ben er bijna uit denk ik..
Zou op zich wel leuk zijn om te kijken welke van de 4/5 manieren nou het snelste is om dit te doen :P


function BuildMenu($rows, $id = 0, $level = 0) {

$numerOfElements = 0;
$menu = "";

for($i = 0; $i < sizeof($rows); $i++) {
if($rows[$i]['parent_id'] == $id) {

$categoryid = $rows[$i]['id'];
$menu .= "<li><a href=\"index.php?category=$categoryid\">{$level}{$rows[$i]['name']}</a></li>";
$menu .= $this->BuildMenu($rows, $rows[$i]['id'], $level + 1);

$numerOfElements++;
}
}

if($numerOfElements > 0 && !empty($menu))
{
$menu = "<ul>{$menu}</ul>";
}

return $menu;
}