PHP rekurze - jak vyřešit kategorie a podkategorie

Napsal Mgr.Radovan Kaluža (») 25. 10. 2004 v kategorii PHP/HTML, přečteno: 4065×

Chceme mít kategorii, která má podkategorii a možnost aby každá podkategorie mohla mít další podkategorie.

Pracuji na jedné php aplikaci a potřeboval jsem generovat kategorie a podkategorii, ale tak aby každá podkategorie mohla mít svou další podkategorii. Ukáži Vám část svého řešení. Rozšířené funkce zde neuvádím, ale pouze základní kostru.

Struktura tabulky

Předpokládám, že každá podkategorie má svoji nadkategorii a ta má opět svoji nadkategorii. Aneb každý syn má svého otce, a každý otec má svého otce.... Ale narozdíl od přírody, zde musí být nějaký táta všech tátů. Takového jsem v tabulce označil hodnotou NULA. Což znamená, že je to nejvyšší kategorie.

CREATE TABLE `kat` (
`id` int(11) NOT NULL auto_increment,
`nazev` varchar(250) NOT NULL,
`tata` int(11) NOT NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM;

INSERT INTO `kat` VALUES (1, 'PC', 0);
INSERT INTO `kat` VALUES (2, 'notebooky', 1);
INSERT INTO `kat` VALUES (3, 'monitory', 1);
INSERT INTO `kat` VALUES (4, 'média', 0);
INSERT INTO `kat` VALUES (5, 'ACER', 2);
INSERT INTO `kat` VALUES (6, 'DELL', 2);
INSERT INTO `kat` VALUES (7, 'ACER - komponenty', 5);
INSERT INTO `kat` VALUES (8, 'klávesnice', 1);
INSERT INTO `kat` VALUES (9, 'normální monitory', 3);
INSERT INTO `kat` VALUES (10, 'LCD', 3);
INSERT INTO `kat` VALUES (11, 'bílé klávesnice', 8);
INSERT INTO `kat` VALUES (12, 'černé klávesnice', 8);



Tabulka bude vypadat následovně

id nazev tata
1 PC 0
2 notebooky 1
3 monitory 1
4 média 0
5 ACER 2
6 DELL 2
7 ACER - komponenty 5
8 klávesnice 1
9 normální monitory 3
10 LCD 3
11 bílé klávesnice 8
12 černé klávesnice 8

Ukázka jak to bude fungovat

Otevřete si PŘÍKLAD, a klikněte na PC - notebooky - ACER. Budou se rozbalovat jednotlivé podkategorie. PC a média mají TATU=0, což znamená, že to jsou kategorii, které nemají svého rodiče (nadkategorii). Při kliknutí na PC, se začnou hledat takové prvky, které mají TATA=1 (protože id od kategorie PC=1), nacházíme notebooky, monitory a klávesnice. Při stisku notebooky, hledá to prvky, které mají TATA=2 (protože id notebooky je 2) a nakonec klikáme na ACER, a hledáme prvky které mají TATA=5, a poté již končíme, protože to je poslední podkategorie (neboli, nemají svého syna :-) ).

Soubor fce/kat.php

V adresáři fce vytvořte soubor kat.php

<?
function posloupnost_k_obsahu($tata)
{
$maximum_urovni=100; // ochrana proti zacykleni
do
{
$dotaz_na_tatu = "SELECT id,tata FROM `kat` WHERE id=$tata LIMIT 1"; //info o prvku

$nacti_tatu = mysql_query($dotaz_na_tatu);
$tata = MySQL_Fetch_Array($nacti_tatu);
$id = $tata["id"];
$tata = $tata["tata"]; //nadrazeny prvek,neboli tata

if ($id > 0) $strom[]=$id; // pridame prvek do stromu
$urovni++;
}
while ( ($tata != 0) and ($urovni < $maximum_urovni));
//to neni nejvyssi prvek, coz znamena ze tata by byl roven nule
//anebo jsme se nezacyklili, muzeme pokracovat

$strom[]=0; //pridame na konec nulty prvek, tatu vsech tatu :-)
return array_reverse($strom); //a otocime
}


function mezera($velikost)
{
$velikost=$velikost*1;
$mezera="";
for ($i=0;$i<=$velikost;$i++) $mezera = $mezera."&nbsp;&nbsp;";
return $mezera;
}



function vypis($kdo,$strom)
//potrebujem strom, ktery urci cestu k aktualni kategorii
{

if ( count($strom)==($kdo+1) )
//je to posledni prvek, tedy budeme jej rozbalovat a skoncime
{
$posledni = count($strom)-1;
$dotaz_na_polozku[$kdo] = "SELECT * FROM `kat` WHERE tata=$strom[$posledni]";
}
else
$dotaz_na_polozku[$kdo] = "SELECT * FROM `kat` WHERE tata=$strom[$kdo]";
// bezny prvek, vypiseme vsechny prvky z teto urovne

$nacti_polozku[$kdo] = mysql_query($dotaz_na_polozku[$kdo]);

while ($polozka[$kdo] = MySQL_Fetch_Array($nacti_polozku[$kdo]))
{
$nazev_p = $polozka[$kdo]["nazev"];
$id_p = $polozka[$kdo]["id"];
$tata_p = $polozka[$kdo]["tata"];
$space = mezera($kdo);
if (strlen($nazev_p)>0) print " $space <a href='index.php?ak=$id_p'>$nazev_p </a> <br>";

if ( (count($strom)>=($kdo+1)) and ($strom[$kdo+1]==$id_p)) vypis($kdo+1,$strom);
// pokud nejsme na konci stromu AND
// zrovna tento prvek je uzel, ktery se tyka prvku, ktery vykreslujeme
// potom posuneme se o prvek dal a vykreslime dalsi cast stromu
// je to rekurze, a tudiz se vratime zpet a dokreslime zbytek

}
}

?>

funkce mezera
neudělá nic jiného, než že vytvoří určitou pevnou mezeru, není třeba dále popisovat (v projektu řeším definováním CSS, např. .uroven1, .uroven2 ....)

funkce posloupnost_k_obsahu($tata)
zjistí, jakou cestou musíme dojít ke kořenu stromu (k hlavní nadkategorii) a poté při hlavní rekurzivní funkci toto pole procházíme, pole si můžete prohlídnou na pod čaru v v příkladu.

funkce vypis($kdo,$strom)
hlavní funkce, využívající rekurzi a strom, který jsme vytvořili v předchozí funkci, doporučuji do výpisů funkce dodat výpis ID, tata, úroveň, abyste zjistili, jak algoritmus funguje. Kdo však zná pojem rekurze, nemělo by to být složité.

Soubor nastaveni.php

Vytvořte soubor nastaveni.php a upravte si proměnné. Pokud pojedete na lokálním PC a budete využívat databázi eshop, pak by tento soubor mohl vypadat následovně

<?
$SQL_Server = "localhost";
$SQL_Uzivatel = "";
$SQL_Heslo = "";
$Databaze = "eshop";
?>

Soubor index.php

<?
require("nastaveni.php");
require("fce/kat.php");
$pripoj = MySQL_Connect($SQL_Server, $SQL_Uzivatel, $SQL_Heslo) or Die(MySQL_Error());
MySQL_Select_Db($Databaze) or Die(MySQL_Error());


$aktualni_kat=$_REQUEST["ak"]*1; //nacteni promenne

$strom = posloupnost_k_obsahu($aktualni_kat); // zjisteni posloupnosti ke korenove kategorii

vypis(0,$strom); //vypsani

print "<hr>A jak vypadá pole strom<br>";
print "<pre>";
print_r($strom);


MySQL_Close($pripoj);
?>

Index.php, není možná ani potřeba komentovat, snad jen pro ty co neznají funkci
print_r - vytiskne pole, proměnnou v nějaké trochu více srozumitelnější podobě. Doporučuji nezkopírovat bezhlavě kód, ale vzít do ruky tužku a papír a snažit se pochopit jak algoritmus funguje. Bude-li mít někdo nějaké doplnění, budu jen rád.

Štítky: rekurze
Facebook Twitter Topčlánky.cz Linkuj.cz

Komentáře

Zobrazit: standardní | od aktivních | poslední příspěvky | všechno
ondrusu z IP 88.100.231.*** | 3.4.2012 20:12
Zdravím mám dotaz. Když ještě nekliknu na podkategorii tak mi to píše tohle: : Undefined offset: 2 ... ale script funguje, prosím nevíte jak to ošetřit, trošku jsem začátečník v PHP rekurzivních funkcích.
Děkuji smile
Jirka z IP 83.240.23.*** | 27.12.2011 13:32
Rád bych se zeptal, jak je to s náročností na DB pro rozsáhlejší menu případně návštěvností např. pro 500 lidí online ?
Není tam velké množství dotazů ?
Vládik z IP 94.230.156.*** | 13.7.2011 12:57
Díky moc kámo!!!
Přispůsobil jsem ty funkce svým potřebám a i vylepšil je a funguje to libooozně smile !!! Je to web zdekoupim.cz .

Fakt díky a hodně úspěchu Všem smile !
juraj z IP 88.212.36.*** | 13.12.2009 20:46
Prajem pekny den
priklad je dobry,mozte poradit ako upravit kod ,aby zobrazovalo cele kategorie bez klikania,aby pozlozky pasovali pod seba ?dakujem
| 14.12.2009 14:21
Jak přesně myslíte pasovali?Máte na mysli řazení?
juraj z IP 88.212.36.*** | 14.12.2009 14:39
Mal som na mysli asi takto,ze by menu bolo trvale rozbalene
ako hlavna kategoria moja predstava:
  PC
    notebooky
    ACER
    DELL
    monitory
      normální monitory
        LCD
MEDIA
dakujem za rady


Nový komentář

Téma:
Jméno:
Notif. e-mail *:
Komentář:
  [b] [obr]
Odpovězte prosím číslicemi: Součet čísel jedna a nula