web-dev-qa-db-fra.com

Sortie wp_nav_menu personnalisée (affichant tous les éléments enfants de l'élément de menu supérieur de la branche actuelle)

Je voudrais créer une fonction de marcheur, ou modifier quelque peu le wp_nav_menus actuel afin qu'il puisse produire une sortie personnalisée. Imaginons cette structure:

menu item 1
menu item 2
 submenu item a
 submenu item b
menu item 3
 submenu item c
 submenu item d
 submenu item e

La partie la plus facile consiste à afficher à un endroit unique les éléments du menu principal (menu principal 1, 2, 3). Vous pouvez le faire avec wp_nav_menus, nous n’avons donc pas besoin de le coder.

La partie problématique est d'afficher ailleurs les éléments de sous-menu de l'élément principal actuellement ... Donc si l'utilisateur est sur la page 'élément de menu 1', rien ne s'affiche. Si l'utilisateur est sur la page 'élément de menu 2', notre "nouveau" menu personnalisé indique:

submenu item a
submenu item b

Les mêmes éléments de menu sont rendus lorsque l'utilisateur se trouve sur l'une des deux pages précédentes (éléments de menu).

Lorsque l'utilisateur clique sur l'élément de menu 3 et visite sa page cible, il voit les éléments de sous-menu c, d, e, comme après avoir cliqué sur l'un de ces éléments de sous-menu.

Si le menu contient des éléments de troisième niveau (et plus profonds), le menu personnalisé doit afficher tous les éléments enfants de l'élément de menu le plus en haut, à côté de cet élément de haut lui-même (qui sera répertorié dans la partie supérieure du site décrit en premier lieu).

Existe-t-il un moyen de créer une telle fonctionnalité?

L'idée est proche de: Afficher une partie/branche de menu elle doit simplement être dynamique et afficher les éléments enfants de la branche de menu actuelle/tous les éléments enfants de l'élément de branche supérieure du menu s'ils existent.

Autant que je sache, je ne peux pas utiliser get_ancestors, car cela ne fonctionne qu'avec les taxonomies hiérarchiques, et nous ne parlons pas ici de menu créé à partir d'une structure hiérarchique post/pages, mais avec l'utilisation de l'éditeur de menus.

RESOLU: Il semble que j'ai pu créer des fonctions correctes combinant peu de méthodes:

/**
 * The API function, just a wrap for wp_nav_menu adding filter first and removing after
 */ 
function filtered_nav_menu( $args = array() ) {
    $echo = isset($args['echo']) ? (bool)$args['echo'] : true;
    $args['echo'] = false;
    add_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
    $menu = wp_nav_menu( $args );
    remove_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
    if ( $echo ) echo $menu;
    else return $menu;
}

/**
 * The filter callback, return the filtered elements
 */
function gmfnv_filter( $items ) {
    $found_top_parent_ID = false;
    foreach ($items as $item) {
        if ( ($item->menu_item_parent == 0 && $item->current_item_ancestor == 1) || ($item->menu_item_parent == 0 && $item->current == 1) ) {
            $found_top_parent_ID = $item->ID;
        }
    }
    $children  = submenu_get_children_ids( $found_top_parent_ID, $items );
    foreach ( $items as $key => $item ) {
        if ( ! in_array( $item->ID, $children ) )
            unset($items[$key]);
    }
    return $items;
}

/**
 * Helper function: return children of an element using wp_filter_object_list
 */
function submenu_get_children_ids( $id, $items ) {
    $ids = wp_filter_object_list( $items, array( 'menu_item_parent' => $id ), 'and', 'ID' );
    foreach ( $ids as $id ) {
        $ids = array_merge( $ids, submenu_get_children_ids( $id, $items ) );
    }
    return $ids;
}

Que fait-il? Il recherche l’ancêtre actuel du haut de la branche en examinant chaque élément $ items et en vérifiant:

($item->menu_item_parent == 0 && $item->current_item_ancestor == 1) || ($item->menu_item_parent == 0 && $item->current == 1)

Cette condition doit être composée de 2 parties, car l'élément de menu de niveau supérieur peut également être actuel.

Si $ item de niveau supérieur est trouvé, alors la fonction submenu_get_children_ids est utilisée pour lister tous ses éléments enfants.

3
Marcin Bobowski

L'idée

Au lieu de créer un lecteur personnalisé, je pensais qu'il était plus facile de filtrer les éléments à l'aide du filtrewp_nav_menu_objectshook.

Ce crochet est défini dans /wp-includes/nav-menu-templates.php et lorsqu'il est déclenché, il passe aux fonctions qui lui lient un tableau $sorted_menu_items qui contient tous les éléments du menu en cours d'impression. Si la fonction modifie ce tableau, ajoute ou supprime des éléments, le menu sera modifié.

La méthode

Si le filtre à wp_nav_menu_objects est appliqué directement, all menus seront filtrés, alors je pensais qu'il valait mieux créer une fonction qui encapsule wp_nav_menuajoutant le filtre avant appelant wp_nav_menu et supprimez-le après: de cette manière, seul le menu souhaité est filtré.

Le flux de travail du filtre

  1. Faire un cycle de tous les éléments passés par le crochet de filtre wp_nav_menu_objects
  2. Créez deux tableaux d'assistance: un seul d'ID parents, un avec des éléments tels que $itemid => $parentid
  3. En boucle, vérifiez également que l'URL de l'élément correspond à l'URL actuelle
  4. si l'URL ne correspond pas, ne renvoyer que les éléments parents
  5. si l'URL correspond, à l'aide des tableaux d'assistance créés, retourne les éléments recherchés

Le code

Le code que j'ai écrit pour cela utilise 5 fonctions, donc je crée un plugin pour tout contenir, ici le code:

<?php
/**
 * Plugin Name: Filtered Nav Menus
 * Author: Giuseppe  Mazzapica
 * Plugin URI: http://wordpress.stackexchange.com/questions/118720/
 * Author URI: http://wordpress.stackexchange.com/users/35541/
 */

/**
 * The API function, just a wrap for wp_nav_menu adding filter first and removing after
 */ 
function filtered_nav_menu( $args = array() ) {
  $echo = isset($args['echo']) ? (bool)$args['echo'] : true;
  $args['echo'] = false;
  add_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
  $menu = wp_nav_menu( $args );
  remove_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
  if ( $echo ) echo $menu;
  else return $menu;
}

/**
 * The filter callback, return the filtered elements
 */
function gmfnv_filter( $items ) {
  $found = false;
  $parents = $items_tree = $allowed = array();
  $all_items = $items;
  while ( ! empty( $items ) ) {
    $item = array_shift( $items );
    $items_tree[$item->ID] = $item->menu_item_parent;
    if ( (int) $item->menu_item_parent == 0 ) $parents[] = $item->ID;
    if ( isset($item->current) && $item->current ) $found = $item->ID;
  }
  if ( ! $found ) {
    $ids = $parents;
  } else {
    $tree = gmfnv_get_tree( $found, $all_items, $items_tree );
    $ids = array_merge( $parents, $tree );
  }
  foreach ( $all_items as $item ) {
    if ( in_array( $item->ID, $ids ) ) $allowed[] = $item;
  }
  return $allowed;
}


/**
 * Helper function: take the matched element if and the helper array and
 * return the item ancestors by gmfnv_get_parents,
 * and the children of these ancestors returned by gmfnv_get_parents
 * using gmfnv_get_parents
 */
function gmfnv_get_tree( $test, $items, $tree ) {
  $parents = gmfnv_get_parents( $test, $items );
  $parents[] = $test;
  $n = array();
  foreach ( $parents as $parent ) {
    $n = array_merge( $n, gmfnv_get_childrens( $parent, $tree ) );
  }
  return array_unique( $n );
}


/**
 * Helper function: return ancestors of an element using the helper array
 */
function gmfnv_get_parents( $test, $items ) {
  $parents = array();
  foreach( $items as $item ) {
      if (
        (isset($item->current_item_ancestor) && $item->current_item_ancestor)
        || (isset($item->current_item_ancestor) && $item->current_item_ancestor)
      ) $parents[] = $item->ID;
  }
  return $parents;
}


/**
 * Helper function: return children of an element using the helper array
 */
function gmfnv_get_childrens( $test, $tree ) {
  $children = array();
  foreach ( $tree as $child => $parent ) {
    if ( $parent == $test ) $children[] = $child;
  }
  return $children;
}

Comment

Créez un fichier contenant ce plugin mis dans le dossier plugins et activez-le.

Lorsque vous souhaitez filtrer un menu selon vos besoins, utilisez plutôt wp_nav_menu( $args ), puis utilisez

filtered_nav_menu( $args );

Avertissement

Code fourni tel quel, aucune garantie, mais juste testé rapidement sur PHP 5.4, WP 3.7 avec vingt-quinze thèmes actifs, et aucun autre plugin: cela a fonctionné dans ce Cas.

5
gmazzap