Le code HTML généré par certaine fonction WordPress est parfois pratique, mais dans le cas de la gestion d’un menu, la DOM générée est souvent très loin de ce dont j’ai besoin.
Au moment de l’utilisation des menus WordPress, mon intégration HTML / CSS est déjà faite (dans une pattern library). Je préfère ne pas refaire mon menu pour ‘coller’ à la DOM générée par WordPress (quand bien même se serait possible).
J’ai donc développé une sur-couche en PHP pour permettre de manipuler et parcourir facilement les éléments de mon menu.
Mais avant de voir ça, commençons par un petit rappel.
Pour créer un menu personnalisé il faut (dans votre fichier functions.php évidement) rajouter ce petit bout de code :
//Créer un emplacement pour notre menu
register_nav_menus( array(
'menu-header' => 'Navigation principale'
));
Ensuite, pour l’afficher, on va rajouter dans notre fichier header.php :
//Afficher notre menu
$defaults = array(
'theme_location' => 'menu-header',
'menu' => '',
'container' => 'div',
'container_class' => '',
'container_id' => '',
'menu_class' => 'menu',
'menu_id' => '',
'echo' => true,
'fallback_cb' => 'wp_page_menu',
'before' => '',
'after' => '',
'link_before' => '',
'link_after' => '',
'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>',
'depth' => 0,
'walker' => ''
);
wp_nav_menu($defaults);
La fonction wp_nav_menu
permet d’afficher notre menu et de modifier dans quel contexte DOM il s’affiche (class, id du conteneur, …). Voir la documentation à ce sujet.
wp_nav_menu_args
pour modifier / ajouter des classes css, mais on ne change pas la DOM.Comme ces solutions ne me convenaient pas, j’ai donc développé une sur-couche, voyant ça par étapes.
//Menu location
$menuLocation = 'menu-header';
//Si le menu existe bien
if (($locations = get_nav_menu_locations())
&& isset($locations[$menuLocation])){
//Alors on récupère l'objet nav menu object
$menuObject = wp_get_nav_menu_object($locations[$menuLocation]);
//Et les élements de l'objet
$menuItems = wp_get_nav_menu_items($menuObject->term_id);
}else{
throw new Exception("Le menu ".$menuLocation." n'existe pas.");
}
On récupère un tableau d’objets, de type WP_Post. On y trouve les informations essentielles de chaque entrée du menu : titre, url, identifiant et identifiant du parent, …
A titre d’exemple, voici un beau print_r
:
WP_Post Object
(
[ID] => 76
[post_author] => 1
[post_date] => 2015-06-02 08:44:28
[post_date_gmt] => 2015-06-02 07:44:28
[post_content] =>
[post_title] => Accueil
[post_excerpt] =>
[post_status] => publish
[comment_status] => open
[ping_status] => open
[post_password] =>
[post_name] => accueil
[to_ping] =>
[pinged] =>
[post_modified] => 2015-06-08 11:33:22
[post_modified_gmt] => 2015-06-08 10:33:22
[post_content_filtered] =>
[post_parent] => 0
[guid] => http://192.168.33.10/lab/?p=76
[menu_order] => 1
[post_type] => nav_menu_item
[post_mime_type] =>
[comment_count] => 0
[filter] => raw
[db_id] => 76
[menu_item_parent] => 0
[object_id] => 76
[object] => custom
[type] => custom
[type_label] => Lien personnalisé
[title] => Accueil
[url] => http://192.168.33.10/lab/
[target] =>
[attr_title] =>
[description] =>
[classes] => Array
(
[0] =>
)
[xfn] =>
)
On peut donc parcourir et récupérer les informations qui nous intéresse :
//Parcourir les items du menu
foreach((array) $menuItems as $key => $menuItem){
//Data
$id = (int) $menuItem->ID;
$parentId = (int) $menuItem->menu_item_parent;
$title = $menuItem->title;
$permalink = $menuItem->url;
}
Par contre, on va voir du mal à gérer l’affichage correctement sans faire une grosse tambouille.
En effet, les objets WP_Post en contiennent pas une notion essentielle pour construire notre DOM, la notion de niveau (ou profondeur) qui nous permettrait de gérer l’imbrication de ul
et li
par exemple.
On va donc transformer toussa en beaux objets imbriqués, facile à parcourir.
Elle contient :
Ci-dessous un exemple commenté de leurs utilisation pour un menu à 2 niveaux :
//Nouvel objet de custom menu
//Avec en paramètre la location du menu que l'on veut récupérer
$menuCollection = new CustomMenuCollection('menu-header');
//On récupère les items
$menuCollectionItems = $menuCollection->getItems();
//Et on commence l'affichage
echo '<nav><ul>';
//Parcoure des items
foreach ($menuCollectionItems as $customMenuItem){
//On récupère collection et items enfant
$customMenuItemCollection = $customMenuItem->getCustomMenuCollection();
$customMenuItemCollectionItems = $customMenuItemCollection->getItems();
//On compte le nombre d'enfants de la collection de l'item courant
$collectionCount = count($customMenuItemCollectionItems);
//Si l'item n'a pas d'enfant
//C'est le premier niveau
if($collectionCount == 0){
echo '<li class="item item-lvl1">';
echo '<a href="'.$customMenuItem->getPermalink().'">'.$customMenuItem->getTitle().'</a>';
echo '</li>';
}
//Sinon si l'item à des enfants
//C'est le deuxième niveau
else{
echo '<li class="item item-lvl1 item-hassubnav">';
echo '<ul>';
//On parcoure les items de deuxième niveau
foreach ($customMenuItemCollectionItems as $customMenuItem2) {
echo '<li class="item item-lvl2">';
echo '<a href="'.$customMenuItem2->getPermalink().'">'.$customMenuItem2->getTitle().'</a>';
echo '</li>';
}
echo '</ul>';
echo '</li>';
}
}
echo '</ul></nav>';
Je ne décris pas le fonctionnement de ces 2 classes PHP dans cet article, le code me semble suffisamment parlant et commenté (mais n’hésitez pas si vous avez des questions).