PHP – Costruire un menu dinamico con pagine a qualsiasi livello di profondità – Guida passo per passo

Ci sono diversi metodi per costruire un menu o una sidebar, una barra di navigazione laterale che mostra l’elenco di categorie e sottocategorie (o di pagine e sottopagine).
Il codice da usare dipende da diversi fattori, quali la conoscenza o meno a priori del livello massimo di profondità delle pagine e il fatto che l’amministratore possa gestire a sua discrezione il contenuto delle pagine.

Nel caso in cui le categorie sono “fisse” e scritte staticamente all’interno della pagina, allora il problema è minimo: vi basta sistemare la grafica e indentare a piacere le categorie interne. Ma cosa fare quando le categorie sono prese da un database e non si conosce il livello di profondità in cui si arriva?
E’ una situazione che capita spesso a chi crea siti web e dota il cliente di un pannello di controllo attraverso il quale può generare, a volontà, categorie e sottocategorie per popolarle poi con i prodotti.

Nell’articolo mostrerò come comporre una barra di navigazione (o un menu) il cui livello di profondità non deve avere un limite imposto.
Nell’esempio le categorie e le sottocategorie sono prese dal database e hanno una struttura gerarchica molto semplice: la sottocategoria avrà come riferimento, in un campo, l’id della categoria padre.

La grafica è minimale, ma nello script da scaricare troverete tutti i file già pronti e con le classi css già implementate, che potrete modificare facilmente attraverso il foglio di stile esterno.
La sidebar così costruita può essere adattata senza problemi all’interno del vostro sito.

Download script

La struttura di base – le categorie nel database

Nello script da scaricare troverete il dump della tabella categorie, che dovrete importare all’interno del vostro database. Naturalmente dovrete preoccuparvi di stabilire una connessione con il vostro database (il file config.inc.php contiene già i parametri per una connessione locale, che dovrete modificare all’occorrenza).
La tabella categorie ha questa struttura:

1
2
3
4
5
6
CREATE TABLE IF NOT EXISTS `categorie` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nome` varchar(255) NOT NULL,
  `padre` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;

Niente di complicato. L’id è il campo identificativo (univoco) della categoria e padre è l’id corrispondente dell’eventuale categoria padre: se quest’ultimo è impostato a 0, significa che la categoria non ha padre ed è quindi una categoria principale.

Nel dump troverete la tabella già popolata e comporrà idealmente questa gerarchia:

1
2
3
4
5
6
7
Computer
-- Desktop
---- IBM
-- Notebook
---- Acer
---- Dell
---- Sony

Il nostro menu apparirà come questa gerarchia. Le categorie saranno stampate a video in ordine alfabetico.
Cliccando su una categoria, si apriranno le sottocategorie del livello immediatamente inferiore. Alle categorie aperte (e alle loro categorie padre) sarà attribuita la classe css “attivo”, che permetterà di formattarle in modo diverso e quindi di metterle in evidenza.
L’id della categoria attiva sarà passato come parametro get nel link del browser: sarà in base a questo id che potremmo trovare, ricorsivamente, i padri e i figli della categoria cercandoli nel database.

Capite le basi, vediamo adesso nei dettagli il codice della pagina.

La funzione per cercare gli id dei padri

Per la realizzazione del menu, ci sarà utile conoscere l’id dei padri della categoria corrente. Diamo un’occhiata al codice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function cerca_padri($id){
	global $db;

	$variabile=""; //immagazzino gli id dei padri in questa stringa, separandoli con il carattere "-"

	$query=mysql_query("select * from categorie where id=".$id." order by nome", $db);
	$row=mysql_fetch_array($query);
	if($row['padre']!=0){
		$variabile.=$row['padre']."-";
		$variabile.=cerca_padri($row['padre']);
	}

	return $variabile;
}

Facendo passare l’id della categoria come parametro, la funzione si preoccuperà di immagazzinare gli id del padre in una variabili. Ricorsivamente, per ognuno di questi padri si richiamerà la stessa funzione cerca_padri(), che aggiungerà alla variabile precedente anche l’id di suo padre (separandoli tra loro con il carattere “-“). E così via, fino a quando non si arriverà alla categoria principale (che cioè avrà il campo $row['padre']!=0).

A cosa ci serve questa procedura? Poniamo di essere all’interno della categoria Computer => Notebook => Acer. Nel get avremo l’id relativo a Acer, ma non avremo nessun riferimento ai suoi padri. Se volessimo attaccare la classe del css “attivo” anche ai padri, la funzione cerca_padri() è quello che fa per noi.

Questa è la funzione di base, che come vedete chiama ricorsivamente se stessa. Come la richiamiamo? Bastano poche righe, che metteremo nella nostra pagina prima di ogni altro script:

1
2
3
4
5
6
7
//cerco i padri della sezione corrente e vedo se questa categoria è uno dei padri
$padri=array();
if(isset($_GET['id'])){
	$padri_stringa=cerca_padri($_GET['id']);
	$padri_stringa=trim($padri_stringa, "-"); //toglie l'ultimo carattere separatore
	$padri=explode("-", $padri_stringa); //immagazzino i singoli id padri in un array
}

Semplicemente, abbiamo immagazzinato il risultato della funzione nella stringa $padri_stringa (che sarà del tipo “id1-id2” ecc.) e poi abbiamo preso i singoli id e li abbiamo immagazzinati in un array.
Adesso ci sarà facile controllare che una categoria sia il padre di quella corrente: se il suo id è nell’array, significa che è un padre.

La stampa del menu

Passiamo alla stampa vera e propria del menu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$query=mysql_query("select * from categorie where padre=0 order by nome", $db);
while($row=mysql_fetch_array($query)){	

	//valuto se la categoria è attiva
	//è attiva se siamo all'interno della categoria o in una delle sue sottocategorie
	$attivo=false;
	if( (isset($_GET["id"])) and ( ($_GET["id"]==$row["id"]) or (sizeof($padri)>0 and in_array($row['id'], $padri) ) ) )
		$attivo=true;

	//se la variabile $_GET['id'] è settata e corrisponde all'id della categoria, aggiungo la classe per renderla attiva
	?>
    <div class="sezione <?php if($attivo) echo "attivo";?>"><a href="<?php echo $http;?>index.php?id=<?php echo $row["id"];?>"><?php echo ucfirst($row["nome"]);?></a></div>

    <?php
	if($attivo)
		echo menu_navigazione($row["id"],0);

}

Il codice non è difficile da capire, ma servono due precisazioni.
Innanzitutto, stampiamo le categorie principali (nel nostro caso “Computer”). Per vedere se è una categoria attiva, e quindi se dobbiamo applicargli la classe css “attivo”, facciamo due tipi di controlli: guardiamo se il suo id corrisponde a quello passato nel get e, in alternativa, controlliamo se il suo id è nell’array dei padri che abbiamo costruito nella sezione precedente.

La seconda precisazione riguarda le sottocategorie. Dopo aver stampato una categoria principale, se questa fa parte degli attivi dobbiamo stampare le sue sottocategorie.
Per fare questo ci serve un’ultima funzione: menu_navigazione(), a cui passeremo come parametri l’id della categoria e il suo livello di profondità (trovandoci nella prima categoria il livello di profondità è 0: in seguito, la funzione aumenterà di 1 il livello a mano a mano che scenderà di livello).

La funzione che stampa le sottocategorie

Un ultimo sforzo e ci siamo. Vediamo il codice della funzione menu_navigazione():

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
function menu_navigazione($id, $livello){

	global $http, $db, $padri;
	$livello++; //la sottocategoria ha profondità del padre +1

	//Indentazione delle sezioni in base al livello (riduco di conseguenza la lunghezza del div)
	$spazio=0;
	for($i=0; $i<$livello; $i++)
		$spazio+=10;
	$width=210-$spazio;

	$variabile="";

	$query=mysql_query("select * from categorie where padre=".$id." order by nome", $db);
	if(mysql_num_rows($query)>0){
		while($row=mysql_fetch_array($query)){

			//valuto se la categoria è attiva
			//è attiva se siamo all'interno della categoria o in una delle sue sottocategorie
			$attivo=false;
			if( (isset($_GET["id"])) and ( ($_GET["id"]==$row["id"]) or (sizeof($padri)>0 and in_array($row['id'], $padri) ) ) )
				$attivo=true;

			$variabile.="<div class=\"sottosezione";
			if($attivo) $variabile.=" attivo";
			$variabile.="\" style=\"padding-left:".$spazio."px; width:".$width."px\"><a href=\"".$http."index.php?id=".$row["id"]."\">".ucfirst($row["nome"])."</a></div>";

			//reitero la funzione per cercare eventuali sottocategorie a questa sottocategoria
			if($attivo)
				$variabile.=menu_navigazione($row["id"],$livello);
		}
	}

	return $variabile;
}

Se avete notato, è molto simile alla stampa della categoria principale.
Qui infatti non facciamo altro che prendere tutte le sottocategorie e stamparle: per ognuna di queste sottocategorie, controlliamo se ha a sua volta sottocategorie e le stampiamo. E così via fino a raggiungere il massimo livello di profondità.

Una sola annotazione va fatta per la variabile $livello. A ogni reitero della funzione menu_navigazione() viene incrementata di 1 e, pertanto, farà capire alla funzione di trovarsi in un livello più basso.
L’utilità è evidente: conoscendo il livello di profondità, possiamo gestire la grafica a nostro piacimento. Nell’esempio, ho incremento l’indentazione dei link in base alla loro profondità.

Etichette
Etichette:, ,
Ultimi Commenti
  1. Andrea

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.