[PHP / MySQL] Recursief verwerken van resultset

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
In mijn CMS'je haal ik op elke pagina een navigatie-boom op met behulp van een recursieve functie. Voor ik deze functie aanroep, bepaal ik de array van elementen uit de boom die 'opengeklapt' moeten zijn. Ga er hier van uit dat die array, $path genaamd, er als volgt uitziet:
code:
1
2
3
4
5
6
7
8
Array
(
    [0] => 76 <-- node waar we nu zijn
    [1] => 73
    [2] => 70
    [3] => 68
    [4] => 55 <-- root
)

Vervolgens roep ik de recursieve functie aan met setSubmenu(55, '/cms'):
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
function setSubmenu($parent, $parentUrl, $level=0) {
  global $menu, $path, $trail, $nodes;
        
  if ($level != 0) {
    $endul = '</ul></li>';
  }
  else {
    $endul = '</ul>';
  }
    
  $res_menu = db_query('SELECT node_id, node_name, node_pos 
                        FROM nodes 
                        WHERE node_pid = %d 
                        ORDER BY node_pos ASC', $parent);
        
  if (db_num_rows($res_menu) == 0) {
    $menu .= '</li>';
  }
  else {
    $menu .= '<ul>';

    for ($a=0; $a<db_num_rows($res_menu); $a++) {
      $row_menu = db_fetch_array($res_menu);
      $state    = '';
                        
      if(in_array ($row_menu['node_id'], $path)) {
        $class = $level.'-on';                
        $endli = '';
                
        if ((count($path)-1) != $level) {
          $trail.= ' &gt; <a href="'.$parentUrl.'/'.
                                     $row_menu['node_name'].'">'.
                                     $row_menu['node_name'].'</a>';
        }
        else {
          $trail .= ' &gt; '.$row_menu['node_name'];
          $state  = '-sel';
        }
      }
      else {
        $class = $level.'-off';
        $endli = '</li>';
      }                        
      $menu .= '<li class="'.$class.$state.'"><a href="'.$parentUrl.'/'.
                                                         $row_menu['node_name'].'/">'.
                                                         $row_menu['node_name'].'</a>'.
                                                         $endli;
            
      if(in_array ($row_menu['node_id'], $path)) {
        $newParent = $parentUrl.'/'.arg($level);
        setSubmenu($row_menu['node_id'],$newParent, $level+1);
      }
    }
    $menu .= $endul;
    return $menu;
  }
}

Dit geeft de volgende output:
HTML:
1
2
3
4
5
6
7
8
9
10
over_kwispel
abonnementen
  bejaarden
    mannen
    vrouwen
      mooi
      lelijk <!-- we zijn hier
  jongeren
contact
diensten

Maar nu bedacht ik me ineens - het is onzin om in setSubmenu 5 queries af te draaien. Ik kan immers:
code:
1
2
3
4
5
SELECT node_id, node_name, node_pos FROM nodes WHERE node_pid = 55 ORDER BY node_pos ASC
SELECT node_id, node_name, node_pos FROM nodes WHERE node_pid = 68 ORDER BY node_pos ASC
SELECT node_id, node_name, node_pos FROM nodes WHERE node_pid = 70 ORDER BY node_pos ASC
SELECT node_id, node_name, node_pos FROM nodes WHERE node_pid = 73 ORDER BY node_pos ASC
SELECT node_id, node_name, node_pos FROM nodes WHERE node_pid = 76 ORDER BY node_pos ASC

vervangen door
code:
1
2
3
4
5
SELECT node_id, node_name, node_pos
FROM nodes
WHERE node_pid
IN ( 55, 68, 70, 73, 76 ) 
ORDER BY node_pos ASC

en zo vele queries uitsparen. Ik heb hierover twee vragen:
• MySQL schijnt niet zo sterk te zijn in subqueries; is de tweede optie beter dan hoe het nu werkt?
• Wie kan mij een start geven met hoe ik de resultset van die tweede query in een gelijke boom zet. Ik heb namelijk geen idee hoe te beginnen...

"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.

Reveller schreef op 19 juli 2004 @ 22:40:
• MySQL schijnt niet zo sterk te zijn in subqueries; is de tweede optie beter dan hoe het nu werkt?
Ik zie nergens een subquery... Dus dat tweede kan gewoon.

'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!

  • BrZ
  • Registratie: Maart 2000
  • Laatst online: 14:15

BrZ

Je kan er niet vanuit gaan dat de id's op volgorde staan. Zo maak je het jezelf bv onmogelijk om dingen te verplaatsen en dergelijke.

Je kan het beste even kijken op deze site: http://www.sitepoint.com/article/hierarchical-data-database
Deze structuur is in veel gevallen de makkelijkste manier om mee te werken bij boomstructuren :)

Acties:
  • 0 Henk 'm!

  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
@BrZ - het artikel waar jij naar verwijst is een manier om met boomstructuren te werken die ik al uitgewerkt heb. Ik maak er gebruik van bij een online winkel.

Voor mijn CMS'je werk ik liever met adjacency list, omdat het hierin makkelijker is om nodes om te draaien en door de boom naar boven en beneden te verplaatsen.
BrZ schreef op 19 juli 2004 @ 22:59:
Je kan er niet vanuit gaan dat de id's op volgorde staan. Zo maak je het jezelf bv onmogelijk om dingen te verplaatsen en dergelijke.
Wat bedoel je hier precies mee? Dingen verplaatsen gaat via de admin - deze functie is er slechts voor om een boom aan de bezoeker te tonen. Wat mis ik?

@NMe84 - blijkbaar gebruik ik verkeerde teminologie. Ik dacht dat een IN() statement in een query een subquery inhield, omdat je dezefde query voor 4 id's herhaalt. Het probleem is dat ik met deze query 1 grote resultset terugkrijg. Mijn vraag is hoe ik die op eenzelfde manier kan verwerken als ik nu de 5 aparte resultsets verwerk...

"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!

  • BrZ
  • Registratie: Maart 2000
  • Laatst online: 14:15

BrZ

Reveller schreef op 19 juli 2004 @ 23:14:
Wat bedoel je hier precies mee? Dingen verplaatsen gaat via de admin - deze functie is er slechts voor om een boom aan de bezoeker te tonen. Wat mis ik?
Mijn opmerking klopt ook niet echt, niet helemaal goed gelezen ;)

Maar wat anders:
Hoe kom je in je 2e query aan die 5 ID's? Je doet nu namelijk 5 losse queries, waar je een ID uithaalt, en aan de hand daarvan doe je de volgende query.

Acties:
  • 0 Henk 'm!

  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
BrZ schreef op 19 juli 2004 @ 23:22:
[...]
Hoe kom je in je 2e query aan die 5 ID's? Je doet nu namelijk 5 losse queries, waar je een ID uithaalt, en aan de hand daarvan doe je de volgende query.
Weer niet helemaal goed gelezen denk ik ;) Het gaat voor hier te ver om uit te leggen, maar je kunt het als een gegeven beschouwen dat ik als input een array krijg met daarin die 5 id's. Ik weet op voorhand dus al welk pad we gaan doorlopen; ik zoek alleen een manier hoe ik met die tweede query eenzelfde output kan krijgen als met vijf losse 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!

  • Expander
  • Registratie: Februari 2001
  • Niet online
Shit.

Ik heb pas precies wat je wil voor elkaar gekregen. Voor mijn hiërarchisch menu. Ook omdat ik maar één query wilde. Echter is het denk ik niet op een mooie manier gelukt, misschien ook wel. En ik kan het je ook niet goed uitleggen.

Eerst lees ik de resultset in in een meerdimensionale array, daaruit maak ik een meerdimensionale array met boomstructuur en daaruit de <ul/>.

De source van mijn website staat op internet, gelukkig, dus je kunt mijn code als voorbeeld gebruiken. Even kijken, de viewcvs link naar het betreffende stukje code is http://www.monna.org/cgi-...u.inc?rev=1.8&view=markup .

Leuk om te weten dat ik hiermee misschien mensen help, was het toch niet heel erg tijdsverspilling.

Expanding the inexpandable


Acties:
  • 0 Henk 'm!

  • Macros
  • Registratie: Februari 2000
  • Laatst online: 15-05 16:29

Macros

I'm watching...

Strategie die ik zelf vaak voorgesteld heb is om de boom structuur helemaal te bouwen in een array met 1 SELECT * query. Die array dan naar een file te schrijven. Deze actie doe je alleen als je iets veranderd in de boom. Dus in je admin gedeelte.
Dan als je die file goed hebt gemaakt kan je hem includen en heb je de originele boom structuur als een multidim array 'gratis' (op 1 include na) in je app. Geen query meer nodig.
Die file wordt lekker gecached door je OS, en als de PHP interpreter wat beter wordt, dan wordt de executable ook gecached. Volgens mij is dat de beste afweging tussen flexibiliteit en snelheid.

"Beauty is the ultimate defence against complexity." David Gelernter

Pagina: 1