J'ai un menu défini dans WP Admin qui ressemble à ceci:
Je veux pouvoir afficher tous les liens enfants dans la barre latérale chaque fois que je suis sur une page parent. Par exemple, si l'utilisateur se trouve sur ma page "À propos de nous", je souhaite qu'une liste des 4 liens surlignés en vert apparaisse dans la barre latérale.
J'ai consulté la documentation de wp_nav_menu () et il ne semble pas y avoir de moyen intégré pour spécifier un nœud particulier d'un menu donné à utiliser comme point de départ lors de la génération des liens.
J'ai créé une solution pour une situation similaire qui s'appuyait sur les relations créées par la page parent, mais je recherche une solution utilisant spécifiquement le système de menus. Toute aide serait appréciée.
J'y pensais toujours, je l'ai donc revisitée et mise au point cette solution qui ne dépend pas beaucoup du contexte:
add_filter( 'wp_nav_menu_objects', 'submenu_limit', 10, 2 );
function submenu_limit( $items, $args ) {
if ( empty( $args->submenu ) ) {
return $items;
}
$ids = wp_filter_object_list( $items, array( 'title' => $args->submenu ), 'and', 'ID' );
$parent_id = array_pop( $ids );
$children = submenu_get_children_ids( $parent_id, $items );
foreach ( $items as $key => $item ) {
if ( ! in_array( $item->ID, $children ) ) {
unset( $items[$key] );
}
}
return $items;
}
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;
}
$args = array(
'theme_location' => 'slug-of-the-menu', // the one used on register_nav_menus
'submenu' => 'About Us', // could be used __() for translations
);
wp_nav_menu( $args );
@goldenapples: Your Walker Class ne fonctionne pas. Mais l'idée est vraiment bonne. J'ai créé un walker basé sur votre idée:
class Selective_Walker extends Walker_Nav_Menu
{
function walk( $elements, $max_depth) {
$args = array_slice(func_get_args(), 2);
$output = '';
if ($max_depth < -1) //invalid parameter
return $output;
if (empty($elements)) //nothing to walk
return $output;
$id_field = $this->db_fields['id'];
$parent_field = $this->db_fields['parent'];
// flat display
if ( -1 == $max_depth ) {
$empty_array = array();
foreach ( $elements as $e )
$this->display_element( $e, $empty_array, 1, 0, $args, $output );
return $output;
}
/*
* need to display in hierarchical order
* separate elements into two buckets: top level and children elements
* children_elements is two dimensional array, eg.
* children_elements[10][] contains all sub-elements whose parent is 10.
*/
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( 0 == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
/*
* when none of the elements is top level
* assume the first one must be root of the sub elements
*/
if ( empty($top_level_elements) ) {
$first = array_slice( $elements, 0, 1 );
$root = $first[0];
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( $root->$parent_field == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
}
$current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' ); //added by continent7
foreach ( $top_level_elements as $e ){ //changed by continent7
// descend only on current tree
$descend_test = array_intersect( $current_element_markers, $e->classes );
if ( !empty( $descend_test ) )
$this->display_element( $e, $children_elements, 2, 0, $args, $output );
}
/*
* if we are displaying all levels, and remaining children_elements is not empty,
* then we got orphans, which should be displayed regardless
*/
/* removed by continent7
if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
$empty_array = array();
foreach ( $children_elements as $orphans )
foreach( $orphans as $op )
$this->display_element( $op, $empty_array, 1, 0, $args, $output );
}
*/
return $output;
}
}
Maintenant vous pouvez utiliser:
<?php wp_nav_menu(
array(
'theme_location'=>'test',
'walker'=>new Selective_Walker() )
); ?>
La sortie est une liste contenant l'élément racine actuel et ses enfants (pas leurs enfants). Def: Elément racine: = L'élément de menu de niveau supérieur qui correspond à la page en cours ou est le parent d'une page en cours ou le parent d'un parent ...
Cela ne répond pas exactement à la question initiale, mais presque, car il y a toujours l'élément de niveau supérieur. Cela me convient, car je souhaite que l’élément de niveau supérieur figure dans le titre de la barre latérale. Si vous souhaitez vous en débarrasser, vous devrez peut-être remplacer display_element ou utiliser un analyseur HTML.
Bonjour @jessegavin :
Les menus de navigation sont stockés dans une combinaison de types de publication personnalisés et de taxonomies personnalisées. Chaque menu est stocké sous la forme d'un terme (c'est-à-dire "À propos du menu" , trouvé dans wp_terms
) d'une taxonomie personnalisée (i.e. nav_menu
, trouvé dans wp_term_taxonomy
.)
Chaque élément du menu de navigation est stocké en tant que poste de post_type=='nav_menu_item'
(c'est-à-dire "À propos de l'entreprise" , trouvé dans wp_posts
) avec ses attributs stockés en tant que méta de publication (en wp_postmeta
) en utilisant un préfixe meta_key
de _menu_item_*
où _menu_item_menu_item_parent
est le ID de l'article de menu de navigation parent de votre élément de menu.
La relation entre les menus et les éléments de menu est stockée dans wp_term_relationships
, où object_id
correspond au $post->ID
pour l’élément de menu Navigation et le $term_relationships->term_taxonomy_id
correspond au menu défini collectivement dans wp_term_taxonomy
et wp_terms
.
Je suis presque sûr qu'il serait possible de hook 'wp_update_nav_menu'
et 'wp_update_nav_menu_item'
de créer des menus dans wp_terms
et un ensemble parallèle de relations dans wp_term_taxonomy
et wp_term_relationships
, où chaque élément de menu de navigation comportant des éléments de sous-menu de navigation devient son propre menu de navigation.
Vous voudriez également hook 'wp_get_nav_menus'
(que j’ai suggéré d’ajouter à WP 3.0 sur la base d’un travail similaire que je faisais il ya quelques mois) pour s’assurer que vos menus de navigation générés ne sont pas affichés pour être manipulés par l'utilisateur dans l'administrateur, sinon ils se désynchroniseraient très rapidement et vous auriez un cauchemar de données sur votre main.
Cela semble amusant et utile, mais c’est un peu plus de code et de tests que ce que je peux me permettre de faire maintenant, en partie parce que tout ce qui synchronise les données a tendance à être un PITA quand il s’agit de résoudre tous les bugs (Et parce que les clients payants me poussent à faire avancer les choses. :) Mais muni des informations ci-dessus, je suis un développeur de plugin WordPress motivé qui pourrait le coder s'il le voulait.
Bien sûr, vous réalisez maintenant que si vous le codez, vous êtes obligé de le poster ici pour que nous puissions tous profiter de vos largesses! :-)
Ceci est une extension du marcheur qui devrait faire ce que vous cherchez:
class Selective_Walker extends Walker_Nav_Menu
{
function walk( $elements, $max_depth) {
$args = array_slice(func_get_args(), 2);
$output = '';
if ($max_depth < -1) //invalid parameter
return $output;
if (empty($elements)) //nothing to walk
return $output;
$id_field = $this->db_fields['id'];
$parent_field = $this->db_fields['parent'];
// flat display
if ( -1 == $max_depth ) {
$empty_array = array();
foreach ( $elements as $e )
$this->display_element( $e, $empty_array, 1, 0, $args, $output );
return $output;
}
/*
* need to display in hierarchical order
* separate elements into two buckets: top level and children elements
* children_elements is two dimensional array, eg.
* children_elements[10][] contains all sub-elements whose parent is 10.
*/
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( 0 == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
/*
* when none of the elements is top level
* assume the first one must be root of the sub elements
*/
if ( empty($top_level_elements) ) {
$first = array_slice( $elements, 0, 1 );
$root = $first[0];
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( $root->$parent_field == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
}
$current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );
foreach ( $top_level_elements as $e ) {
// descend only on current tree
$descend_test = array_intersect( $current_element_markers, $e->classes );
if ( empty( $descend_test ) ) unset ( $children_elements );
$this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
}
/*
* if we are displaying all levels, and remaining children_elements is not empty,
* then we got orphans, which should be displayed regardless
*/
if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
$empty_array = array();
foreach ( $children_elements as $orphans )
foreach( $orphans as $op )
$this->display_element( $op, $empty_array, 1, 0, $args, $output );
}
return $output;
}
}
Basé vaguement sur le code de mfields que j'ai mentionné dans mon commentaire plus tôt. Tout ce qu’il fait est de vérifier dans le menu pour voir si l’élément actuel est (1) l’élément de menu actuel ou (2) un ancêtre de l’élément de menu actuel et ne développe la sous-arborescence en dessous que si l'une de ces conditions est vraie. . J'espère que cela fonctionne pour toi.
Pour l'utiliser, ajoutez simplement un argument "walker" lorsque vous appelez le menu, c'est-à-dire:
<?php wp_nav_menu(
array(
'theme_location'=>'test',
'walker'=>new Selective_Walker() )
); ?>
J'ai mis en place le cours suivant pour moi-même. Il trouvera le parent de navigation supérieur de la page actuelle ou vous pouvez lui attribuer un ID de navigation supérieur cible dans le constructeur du lecteur.
class Walker_SubNav_Menu extends Walker_Nav_Menu {
var $target_id = false;
function __construct($target_id = false) {
$this->target_id = $target_id;
}
function walk($items, $depth) {
$args = array_slice(func_get_args(), 2);
$args = $args[0];
$parent_field = $this->db_fields['parent'];
$target_id = $this->target_id;
$filtered_items = array();
// if the parent is not set, set it based on the post
if (!$target_id) {
global $post;
foreach ($items as $item) {
if ($item->object_id == $post->ID) {
$target_id = $item->ID;
}
}
}
// if there isn't a parent, do a regular menu
if (!$target_id) return parent::walk($items, $depth, $args);
// get the top nav item
$target_id = $this->top_level_id($items, $target_id);
// only include items under the parent
foreach ($items as $item) {
if (!$item->$parent_field) continue;
$item_id = $this->top_level_id($items, $item->ID);
if ($item_id == $target_id) {
$filtered_items[] = $item;
}
}
return parent::walk($filtered_items, $depth, $args);
}
// gets the top level ID for an item ID
function top_level_id($items, $item_id) {
$parent_field = $this->db_fields['parent'];
$parents = array();
foreach ($items as $item) {
if ($item->$parent_field) {
$parents[$item->ID] = $item->$parent_field;
}
}
// find the top level item
while (array_key_exists($item_id, $parents)) {
$item_id = $parents[$item_id];
}
return $item_id;
}
}
Appel de navigation:
wp_nav_menu(array(
'theme_location' => 'main_menu',
'walker' => new Walker_SubNav_Menu(22), // with ID
));
Mise à jour: Je l'ai transformé en plugin. Télécharger ici .
J'avais besoin de résoudre ce problème moi-même et d'écrire un filtre sur les résultats de la recherche de menu. Il vous permet d'utiliser wp_nav_menu
comme d'habitude, mais choisissez une sous-section du menu basée sur le titre de l'élément parent. Ajoutez un paramètre submenu
au menu comme suit:
wp_nav_menu(array(
'menu' => 'header',
'submenu' => 'About Us',
));
Vous pouvez même aller à plusieurs niveaux en mettant des slash dans:
wp_nav_menu(array(
'menu' => 'header',
'submenu' => 'About Us/Board of Directors'
));
Ou si vous préférez avec un tableau:
wp_nav_menu(array(
'menu' => 'header',
'submenu' => array('About Us', 'Board of Directors')
));
Il utilise une version limace du titre, ce qui devrait lui permettre de pardonner des choses comme les majuscules et la ponctuation.
@davidn @hakre Bonjour, j'ai une solution laide sans HTML-Parser ni surcharger display_element.
class Selective_Walker extends Walker_Nav_Menu
{
function walk( $elements, $max_depth) {
$args = array_slice(func_get_args(), 2);
$output = '';
if ($max_depth < -1) //invalid parameter
return $output;
if (empty($elements)) //nothing to walk
return $output;
$id_field = $this->db_fields['id'];
$parent_field = $this->db_fields['parent'];
// flat display
if ( -1 == $max_depth ) {
$empty_array = array();
foreach ( $elements as $e )
$this->display_element( $e, $empty_array, 1, 0, $args, $output );
return $output;
}
/*
* need to display in hierarchical order
* separate elements into two buckets: top level and children elements
* children_elements is two dimensional array, eg.
* children_elements[10][] contains all sub-elements whose parent is 10.
*/
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( 0 == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
/*
* when none of the elements is top level
* assume the first one must be root of the sub elements
*/
if ( empty($top_level_elements) ) {
$first = array_slice( $elements, 0, 1 );
$root = $first[0];
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( $root->$parent_field == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
}
$current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' ); //added by continent7
foreach ( $top_level_elements as $e ){ //changed by continent7
// descend only on current tree
$descend_test = array_intersect( $current_element_markers, $e->classes );
if ( !empty( $descend_test ) )
$this->display_element( $e, $children_elements, 2, 0, $args, $output );
}
/*
* if we are displaying all levels, and remaining children_elements is not empty,
* then we got orphans, which should be displayed regardless
*/
/* removed by continent7
if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
$empty_array = array();
foreach ( $children_elements as $orphans )
foreach( $orphans as $op )
$this->display_element( $op, $empty_array, 1, 0, $args, $output );
}
*/
/*added by alpguneysel */
$pos = strpos($output, '<a');
$pos2 = strpos($output, 'a>');
$topper= substr($output, 0, $pos).substr($output, $pos2+2);
$pos3 = strpos($topper, '>');
$lasst=substr($topper, $pos3+1);
$submenu= substr($lasst, 0, -6);
return $submenu;
}
}
Découvrez le code dans mon plugin ou utilisez-le à vos fins;)
Ce plugin ajoute un widget "Menu de navigation" amélioré. Il offre de nombreuses options qui peuvent être définies pour personnaliser la sortie du menu personnalisé via le widget.
Les fonctionnalités incluent:
J'ai fait un marcheur modifié qui devrait aider! Pas parfait - il laisse quelques éléments vides, mais ça fait l'affaire. La modification concerne essentiellement les bits $ current_branch. J'espère que ça aide quelqu'un!
class Kanec_Walker_Nav_Menu extends Walker {
/**
* @see Walker::$tree_type
* @since 3.0.0
* @var string
*/
var $tree_type = array( 'post_type', 'taxonomy', 'custom' );
/**
* @see Walker::$db_fields
* @since 3.0.0
* @todo Decouple this.
* @var array
*/
var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );
/**
* @see Walker::start_lvl()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param int $depth Depth of page. Used for padding.
*/
function start_lvl(&$output, $depth) {
$indent = str_repeat("\t", $depth);
$output .= "\n$indent<ul class=\"sub-menu\">\n";
}
/**
* @see Walker::end_lvl()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param int $depth Depth of page. Used for padding.
*/
function end_lvl(&$output, $depth) {
global $current_branch;
if ($depth == 0) $current_branch = false;
$indent = str_repeat("\t", $depth);
$output .= "$indent</ul>\n";
}
/**
* @see Walker::start_el()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param object $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param int $current_page Menu item ID.
* @param object $args
*/
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
global $current_branch;
// Is this menu item in the current branch?
if(in_array('current-menu-ancestor',$item->classes) ||
in_array('current-menu-parent',$item->classes) ||
in_array('current-menu-item',$item->classes)) {
$current_branch = true;
}
if($current_branch && $depth > 0) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $value . $class_names .'>';
$attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) .'"' : '';
$attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) .'"' : '';
$attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : '';
$attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) .'"' : '';
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
/**
* @see Walker::end_el()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param object $item Page data object. Not used.
* @param int $depth Depth of page. Not Used.
*/
function end_el(&$output, $item, $depth) {
global $current_branch;
if($current_branch && $depth > 0) $output .= "</li>\n";
if($depth == 0) $current_branch = 0;
}
}
La sortie du menu de navigation inclut de nombreuses classes pour l'élément en cours, l'ancêtre de l'élément en cours, etc. Dans certaines situations, j'ai été en mesure de faire ce que vous voulez faire en laissant toute la sortie de l'arborescence de navigation, puis en utilisant css pour la réduire à seuls les enfants de la page en cours, etc.