Sziasztok! Írtam egy kis szösszenetet és úgy gondoltam, hogy megosztom veletek is, hátha valakinek van valamilyen építő jellegű hozzászólása
Mint az a cikk nevéből kiderülhetett, az alábbiakban nem táplálkozási tanácsokkal kívánok szolgálni, hanem egy sokak által misztikusnak gondolt eljárásról lesz szó.
Mi is az a rekurzió?
A rekurzió igazából nagyjából azt jelenti, hogy egy függvény önmagát hívja meg. Ez hasonlít a ciklusokhoz, de több lehetőséget ad a programod finomhangolására. Egy egyszerű programrészlettel szemléltetném a koncepciót:
PHP kód:
function pluszEgy($szam) {
if($szam<10) {
echo ++$szam, "<br />";
pluszEgy($szam);
} else {
echo 'Kész! <br />';
}
}
A fenti program egy rekurziót használva kiírja a számokat egytől 10-ig (igaz, hogy a feltétel azt vizsgálja, hogy 10-nél kisebb-e, de a programban inkrementálva írja ki a számot, tehát a következő futáskor teszteli az értéket és nem fut le újra 10-nél).
Minek a segítségével építjük fel a menüket?
Most, hogy a kezünkben van egy megfelelő fegyver, már csak muníció kell: a rekurzív menüépítéshez tömböket fogunk használni. A tömböknek van egy pozitív tulajdonsága, ami kifejezetten jó viszonyban van a rekurzióval: több dimenzióban is elhelyezkedhetnek és így megfelelő vizsgálattal (van-e tömb a tömbben?) meghívhatjuk újra a menü építő funkciónkat, ami így alkalmas lesz akár több szintes menürendszerek kialakítására is. Példánkban az alábbi tömböt használjuk:
PHP kód:
$menu_array = array( 'rolunk' => array(
'display' => 'Rolunk' ),
'blog' => array(
'display' => 'Blogunk' ),
'linkek' => array(
'display' => 'Ajanlott linkek',
'sub' => array(
'termekek' => array(
'display' => 'Termekeink',
'url' => 'linkek/#termekek'
),
'szolgaltatasok' => array(
'display' => 'Szolgaltatasaink',
'url' => 'links/#szolgaltatasok',
'sub' => array(
'helyi' => array(
'display' => 'Helyi szolgaltatasok',
'url' => 'links/#helyi-szolgaltatasok'
),
'online' => array(
'display' => 'Online szolgaltatasok',
'url' => 'links/#online-szolgaltatasok'
)
)
)
)
),
'kapcsolat' => array(
'display' => 'Kapcsolat'
));
Igen, ez egy többszintes menü lesz. Minden szinten van egy, vagy több elem. ’display’ jelöli a menü nevét, amit a böngészőben fog látni a felhasználó. ’url’ értelemszerűen a menüponthoz tartozó url-t, illetve ’sub’-al jelöljük azt, ha egy almenüről van szó. Ez alapján tudjuk majd a függvényben eldönteni, hogy kell-e újabb rekurzió, vagy sem.
És hogyan működik?
Maga a funkció nem bonyolult, de elsőre egy picit talán nehezen lesz emészthető azoknak, akik nem szoktak rekurziót használni. Úgy gondoltam, hogy egy az egyben ide teszem a függvényt és akkor nem darabokból kell összeraknod. A könnyebb érthetőség érdekében kommentekkel magyarázom el, hogy mi miért van, így remélhetőleg mindenkinek világos lesz. J
PHP kód:
// Az első menünk biztos nem lesz almenü, ezért $is_sub alapértelmezettként FALSE
function menutEpit($menu_array, $is_sub=FALSE) {
/*
* Ha almenüről van szó, akkor adjunk neki megfelelő
* stílust.
*/
$attr = (!$is_sub) ? 'id="menu"' : 'class="almenu"';
/*
* A már elkészített részeit a menünek a roppant elmés $menu változóba mentjük el
* a webes sztenderdekhez alkalmazkodva nem sorszámozott listában, amit itt megnyitunk:
*/
$menu = "<ul $attr>\n";
/*
* Először végig kell futni a tömb elemein
*/
foreach($menu_array as $id => $elemek) {
/*
* Mivel minden elem egy másik tömb, ezért kell még egy ciklus, amivel végigfutunk
* a tömbben lévő belső tömbön,
* és az elemeit elmentjük változókba
*/
foreach($elemek as $key => $val) {
/*
* Itt jön a varázslat, ha az elem egy másik tömböt tartalmaz,
* akkor egy almenüről van szó, tehát újra meghívjuk a
* menuEpit() függvényt, majd elmentjük egy változóba az eredményt.
*
*/
if(is_array($val)) {
// fontos, hogy itt a második paraméter TRUE lesz, mert ez már egy almenü
$sub = menutEpit($val, TRUE);
}
/*
* ha a feltétel nem teljesül, akkor is létrehozzuk a $sub változót, de NULL értékkel
* ezután pedig a többi elemét a tömbnek elmentjük egy változóba (változó változó)
* tehát $$key = $val-ból pl $display lesz, ha a tömb $display nevű eleménél tartunk
*/
else {
$sub = NULL;
$$key = $val;
}
}
/*
* Ha nincs a belső tömbünkben ’url’ nevű elem (a főmenüben lesz ilyen), akkor az
* elem ID-je lesz az url.
*/
if(!isset($url)) {
$url = $id;
}
/*
* Végül kiírjuk a menü változónkba a tartalmakat. Figyeljük meg a $sub változót,
* melynek, ha lesz értéke, akkor egy új listát fog tartalmazni (ami esetleg további
* listákat tartalmaz stb), viszont megfelel formátumban a webes sztenderdeknek,
* ha viszont értéke NULL, akkor nem változik semmi és nem esik szét a kódunk.
*/
$menu .= "<li><a href=\"$url\">$display</a>$sub</li>\n";
/*
* A változóinkat töröljük, hogy a következő iterációban
* biztosan üresek legyenek
*/
unset($url, $display, $sub);
}
/*
* Végül a $menu változó lesz a visszatérési érték
* a felépítette menüvel
*/
return $menu . "</ul>\n";
}
// Ne felejtsük el, hogy kiírás nincs, tehát ezt még echo-zni kell
echo menutEpit($menu_array);
Összegzés
Miért jó ez a funkció?
Ha már eljutottál idáig, akkor valószínűleg láttad értelmét ennek az algoritmusnak, de esetleg felmerülhet benned, hogy „Oké, de miért építsek többdimenziós tömböket, ha megírhatom az egész menüt kézzel?”
A válasz nagyon egyszerű: Ezt is csak egyszer kell megírnod, viszont sokkal könnyebb utólag módosítani, mint egy hosszú kódban turkálni, valamint megkíméled magad a végeláthatatlan elágazáshalmazoktól. Ezen kívül az is mellette szól, hogy az előző okból kifolyólag kevesebb kóddal jár és áttekinthetőbb, valamint a tömböt, amiből felépíted létre tudod hozni adatbázisban tárolt struktúra alapján is, így megkímélheted magad a tömb megírásától is.
Hogyan bővíthető?
Igazság szerint a fent szemléltetett függvény meglehetősen egyszerű struktúrát hoz létre, tehát ezt tetszés szerint lehet bővíteni. Jó példa erre az, ha hozzáadsz minden menüponthoz egy title attribútumot is (SEO!) a tömbödben és utána csak hozzáteszed a változódat a lista elemek kiírásánál.
Végszó
Remélem, hogy ezzel a kis írással egy olyan megoldást nyújtottam, amit már kerestél egy ideje és hasznos lesz, mint ahogy nekem is hasznos volt, amikor először találkoztam vele. Ha esetleg felmerülne bármilyen kérdésed, vagy hibát találnál, akkor szívesen fogadok minden nemű hozzászólást.
Könyvjelzők