Ajouter un menu à son site avec Zend_Navigation

Un menu créé avec Zend_Navigation

Ca fait un moment que je n'ai pas blogué, je vais me rattraper avec un petit tutoriel sur Zend_Navigation. Je vous propose de réaliser un menu à deux niveaux similaire à celui qu'on trouve par exemple sur le site de Libération, le javascript en moins.

Créer la structure du menu

On va commencer par créer la structure du menu. On peut le faire par le biais d'un tableau ou d'un fichier de configuration (INI ou XML). Comme INI n'est pas trop adapté aux structures arborescentes et que je n'aime pas XML, on va utiliser un tableau :


<?php
return array(
    array(
        
'label' => 'Accueil',
        
'controller' => 'index',
        
'action' => 'index'
    
),
    array(
        
'label' => 'Tour en images',
        
'controller' => 'tour',
        
'action' => 'index',
        
'pages' => array(
            array(
                
'class' => 'first',
                
'label' => 'Carnet d\'adresses',
                
'controller' => 'tour',
                
'action' => 'carnet-adresses',
            ),
            array(
                
'label' => 'Devis',
                
'controller' => 'tour',
                
'action' => 'devis'
            
)
        )
    ),
    array(
        
'label' => 'Tarifs',
        
'controller' => 'tarifs',
        
'action' => 'index'
    
)
);      
?>

La clé label correspond au texte qui sera utilisé pour le lien. Ensuite, on spécifie le contrôleur et l'action à associer. On peut également spécifier une classe CSS qui sera appliquée sur le lien. Les menus secondaires apparaissent dans un sous-tableau (clé pages). Ce tableau peut être stocké dans un fichier PHP qui sera placé dans application/configs. Comme j'ai placé un return au début du fichier, on peut créer un container Zend_Navigation de la façon suivante :

<?php
$pages 
= new Zend_Navigation(include(APPLICATION_PATH '/configs/navigation.php'));
?>

Afficher le menu primaire

On peut ensuite effectuer le rendu du premier niveau du menu :

<?php
echo $this->navigation()->menu()->renderMenu($pages, array('maxDepth' => 0));
?>

On utilise l'option maxDepth pour n'afficher que le premier niveau du menu. Ce dernier est rendu sous la forme d'une liste non ordonnée. La classe CSS navigation est associée à la balise UL. On va donc lui appliquer un style pour la faire ressembler à des onglets :

/* Navigation primaire */
.navigation {
    background-color: #01A0FE;
    height: 24px;
    margin: 0;
    padding: 8px 0 0 0;
    width: 100%;
}
.navigation a {
    background: transparent url('images/tab-lc.png') no-repeat scroll top left;
    color: #FFF;
    display: block;
    font-weight: bold;
    padding: 4px 0 4px 12px;
    text-decoration: none;
}
.navigation a:hover, .navigation a:visited {
    color: #FFF;
}
.navigation li {
    background: transparent url('images/tab-r.png') no-repeat scroll top right;
    display: block;
    float: left;
    margin: 0 0 0 4px; /* Espace entre les onglets */
    padding: 0 12px 0 0;
}
.navigation li.active {
    background-position: bottom right;
}
.navigation li.active a {
    background-position: bottom left;
    color: #000;
}
.navigation li.active a:visited {
    color: #000;
}

On passe les balises LI en display de type block et on les empile sur la même ligne avec float: left. L'élément actif est associé à la classe active, on peut donc s'en servir pour le différencier des autres onglets. Pour les coins arrondis, on utilise la technique des sliding doors avec les sprites suivants :

L'image pour le coté gauche et le centreL'image pour le coté droit

Afficher le menu secondaire

Pour le sous-menu, c'est à peu prés la même chose, sauf qu'on utilise des options différentes pour n'afficher que le niveau 1.

<?php
echo '<div class="sec-nav-wrap">';
echo 
$this->navigation()->menu()->renderMenu(
    
$pages,
    array(
        
'ulClass'          => 'sec-navigation',
        
'minDepth'         => 1,
        
'onlyActiveBranch' => true,
        
'renderParents'    => false
    
)
);
echo 
'</div>';
?>

On enveloppe le menu dans un DIV pour gérer le cas où on n'a pas de sous-menu.

Cas où il n'y pas de menu

Le code CSS :

/* Navigation secondaire */
.sec-nav-wrap {
    background-color: #F5F5F5;
    border-bottom: solid #EEE 1px;
    padding: 2px 0;
    margin: 0;
}
.sec-navigation {
    margin: 0;
    padding: 6px 0;
    font-weight: bold;
    height: 16px;
}
.sec-navigation a, .sec-navigation a:hover, .sec-navigation a:visited {
    border-left: solid #DDD 1px;
    color: #BBB;
    display: block;
    padding: 0 12px;
    text-decoration: none;
}
.sec-navigation li.active a, .sec-navigation li.active a:visited   {
    color: #000;
}
.sec-navigation a.first {
    border-left: 0 none;
}
.sec-navigation li {    
    display: block;
    float: left;
    margin: 0;
}

On ne peut pas insérer du code HTML pour séparer les différents éléments. J'ai donc utilisé une bordure CSS. Pour éviter qu'elle n'apparaisse sur le premier élément, on lui attribue une classe first où en supprime les bordures.

Génération de la carte de site

Zend_Navigation nous permet également de générer le fichier sitemap.xml qui facilite l'indexation pour les moteurs de recherche. On peut le générer dynamiquement ou créer un fichier statique avec un script en ligne de commande comme celui ci :

<?php
#!/usr/bin/php
<?php
// Chemin du répertoire application
define('APPLICATION_PATH'realpath(dirname(__FILE__) . '/../application'));

// Environnement
define('APPLICATION_ENV''development');

// On s'assure que la bibliothèque est bien dans le chemin d'inclusion
set_include_path(realpath(APPLICATION_PATH '/../library'));

// On démarre uniquement le routeur car il doit être initialisé pour que les
// URL puissent être générées
require_once 'Zend/Application.php';
$application = new Zend_Application(
    
APPLICATION_ENV,
    array(
        
'resources' => array(
            
'FrontController' => array(
                
'baseUrl' => 'http://mondomaine.fr'
            
),
            
'Router' => array(
                
'routes' => array(
                    
'default' => array(
                        
'route' => ':controller/:action'
                    
)
                )
            )
        )
    )
);
$application->bootstrap(array('Router'));

// Génère la carte de site dans /public/sitemap.xml
$pages = new Zend_Navigation(include(APPLICATION_PATH '/configs/public-nav.php'));
$sitemapHelper = new Zend_View_Helper_Navigation_Sitemap();
$filename realpath(dirname(__FILE__) . '/../public') . '/sitemap.xml';
file_put_contents(
    
$filename,
    
$sitemapHelper->render($pages)
);
echo 
"Sitemap $filename updated";
?>

Comme vous le voyez, on peut tout à fait utiliser une aide de vue sans vue. C'est un objet standard, on peut l'instancier et s'en servir sans devoir créer un objet Zend_View (attention, ce n'est pas toujours possible avec d'autres helpers). Et avec Zend_Application, on peut démarrer uniquement les ressources nécessaires.

Voilà, c'est fini. Vous disposez à présent d'une méthode pour générer un menu à un ou deux niveaux qui devrait convenir à la majorité des sites.

Commentaires

Ce composant est vraiment intéressant, il faut bien avouer que j'ai un peu de mal à l'intégrer dans un projet un peu plus complexe. Il est peut être un peut trop novateur.

Bonjour,

Pour une meilleure compatibilitée FF et IE 8 je vous invite a changer :

.navigation {
float:left;
background-color: #01A0FE;
height: 30px;
margin: 0;
/* padding: 18px 0 0 0; a enlever */
width: 100%;
}
.navigation li {
background: transparent url('../images/tab-r.png') no-repeat scroll top right;
display: block;
float: left;
margin: 8px 0 0 4px; /* 8px a jouté */
padding: 0 12px 0 0;
}

et :

.navigation a:hover {
color: #000;
background-position: bottom left;
}
.navigation li:hover {
background-position: bottom right;
}

au lieu de :

/*
.navigation li.active {
background-position: bottom right;
}
.navigation li.active a {
background-position: bottom left;
color: #000;
}
.navigation li.active a:visited {
color: #000;
}
*/

Bonjour

J'ai testé ce menu avec la version 1.9.6 et les sous menus ne s'affichent pas. Une idée ?

Merci

Pour l'instant, je suis encore en 1.8. Je n'ai pas encore testé ce code avec la 1.9.

j'ai testé le code avec la version 1.9.6 ... mais moi aussi le menu secondaire ne s'affiche pas =( .. si quelqu'un aurait une solution =)

Je viens de passer en 1.9.6 et ça a l'air de fonctionner... 8-|

J'ai testé sous la 1.9.6 , pas de soucis ça affiche bien les menus et sous menu ... à une chose prés ! les sous menu s'affiche partout, dans quel fichier mets tu le code pour le sous menu ?

Comme discuté avec Jonathan, une solution consiste à tromper Zend, en lui mettant un faux sous-menu, en attendant de trouver une meilleur solution.


return array(
array(
'label' => 'Accueil',
'controller' => 'index',
'action' => 'index',
'pages' => array(array(
'class' =>'first',
'label' => '',
'controller' => '',
'action' => '',
))
),
array(
'label' => 'NatNum',
'module'=>'natnum',
'controller' => 'index',
'pages' => array(
array(
'class' => 'first',
'label' => 'Search on MAH/titulaire',
'module'=>'natnum',
'controller' => 'index',
'action' => 'mah',
),
array(
'label' => 'Deuxième controller',
'controller' => 'tour',
'action' => 'devis'
)
)
));

Bonjour,Bonsoir
ça m'interresse énormément de pouvoir créer des onglets dans mon site web.cependant ,je n'y arrive toujours pas à le faire .Permièrement ,je sais pas où placer le code de menu et de sous menu .et la deuxième chose c'est que je comprends pas l'intéret de la création de la carte de site dans la création des onglets??
Merci à l'avance pour vos réponses.

Le tableau qui définit la structure des menus est placé dans le fichier /application/configs/navigation.php. Quand au code qui effectue le rendu, je le place dans mon layout.

La carte de site, c'est uniquement une possibilité supplémentaire offerte par Zend_Navigation, tu peux très bien créer un menu sans la générer.

Pages

Ajouter un commentaire