Mostrar una porción/rama del árbol del menú usando wp_nav_menu()
Tengo un menú definido en el WP Admin que se ve así:
Quiero poder mostrar todos los enlaces secundarios en la barra lateral cuando estoy en una página principal. Por ejemplo, si el usuario está en mi página "Sobre Nosotros", quiero que aparezca una lista de los 4 enlaces resaltados en verde en la barra lateral.
Revisé la documentación de wp_nav_menu() y parece que no tiene ninguna forma incorporada de especificar un nodo particular de un menú determinado para usar como punto de partida al generar los enlaces.
Creé una solución para una situación similar que se basaba en las relaciones creadas por la página padre, pero estoy buscando una que utilice específicamente el sistema de menús. Cualquier ayuda sería apreciada.
Esto aún estaba en mi mente, así que lo revisité y armé esta solución, que no depende tanto del contexto:
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;
}
Uso
$args = array(
'theme_location' => 'slug-del-menu', // el mismo usado en register_nav_menus
'submenu' => 'Sobre Nosotros', // podría usarse __() para traducciones
);
wp_nav_menu( $args );

¡Encantadora técnica! ¿Puedo preguntar algo posiblemente relacionado con esta: Cómo mostrarías el contenido de esas páginas de submenú listadas en la plantilla?

@daniel.tosaba necesitarás subclasificar o usar filtros en la clase Walker_Nav_Menu
. Como todo lo relacionado con menús, es demasiado para un comentario - ¿por qué no haces una nueva pregunta al respecto?

http://wordpress.stackexchange.com/questions/62758/display-content-of-child-links

Una respuesta fantástica. Muchas gracias. Esto realmente debería ser una opción predeterminada en WordPress.

Hmm, en realidad tengo un problema extraño. Tengo una página llamada "Children's". Y parece que el apóstrofe en la palabra impide encontrar la página. ¿Alguna idea?

@dotty No recuerdo exactamente qué se almacena en el campo por el que filtra el código. Podría ser una versión saneada o con barras de escape del título.

Lo siento, no estoy seguro de lo que quieres decir. ¿Podrías aclararlo por favor?

@dotty podría estar almacenado como Children\'s
o algo así, no tengo este código configurado para probar en este momento...

Parece que esto ya no funciona en WP 4. Recibo este error: Strict Standards: Only variables should be passed by reference in -> on line -> $parent_id = array_pop( wp_filter_object_list( $items, array( 'title' => $args->submenu ), 'and', 'ID' ) );

@gdaniel probó: sí funciona y no tiene nada que ver con la actualización de WP, solo necesitaba un pequeño ajuste para que fuera un código PHP más adecuado :)

Extraño. Todos mis submenús que usaban esa función desaparecieron justo después de actualizar a WP 4. Gracias por echarle un vistazo de todos modos. Verificaré qué más pudo haber ocurrido.

Descubrí mi problema. Estaba usando el argumento menu => "nombre-del-menú" en wp_nav_menu... Eliminé 'menu' y agregué theme_location, y todo volvió a la normalidad.

Hola @Rarst, todavía apareces en primer lugar en muchos sitios de esta respuesta. La versión aceptada debería reescribirse para tener en cuenta los navegadores personalizados, ya que uno podría simplemente filtrar por "current-menu-item", "current-menu-parent" y "current-menu-ancestor" y luego renderizar el submenú. Sin filtros, sin trucos.

@ImperativeIdeas Mi respuesta muestra una rama arbitraria por nombre, no solo la actual.

@goldenapples: Tu Clase Walker no funciona. Pero la idea es realmente buena. Creé un walker basado en tu idea:
class Selective_Walker extends Walker_Nav_Menu
{
function walk( $elements, $max_depth) {
$args = array_slice(func_get_args(), 2);
$output = '';
if ($max_depth < -1) //parámetro inválido
return $output;
if (empty($elements)) //nada que recorrer
return $output;
$id_field = $this->db_fields['id'];
$parent_field = $this->db_fields['parent'];
// visualización plana
if ( -1 == $max_depth ) {
$empty_array = array();
foreach ( $elements as $e )
$this->display_element( $e, $empty_array, 1, 0, $args, $output );
return $output;
}
/*
* necesita mostrarse en orden jerárquico
* separa elementos en dos grupos: elementos de nivel superior y elementos hijos
* children_elements es un array bidimensional, ej.
* children_elements[10][] contiene todos los sub-elementos cuyo padre es 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;
}
/*
* cuando ninguno de los elementos es de nivel superior
* asume que el primero debe ser la raíz de los sub-elementos
*/
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' ); //añadido por continent7
foreach ( $top_level_elements as $e ){ //modificado por continent7
// desciende solo en el árbol actual
$descend_test = array_intersect( $current_element_markers, $e->classes );
if ( !empty( $descend_test ) )
$this->display_element( $e, $children_elements, 2, 0, $args, $output );
}
/*
* si estamos mostrando todos los niveles, y children_elements no está vacío,
* entonces tenemos huérfanos, que deberían mostrarse de todos modos
*/
/* eliminado por 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;
}
}
Ahora puedes usar:
<?php wp_nav_menu(
array(
'theme_location'=>'test',
'walker'=>new Selective_Walker() )
); ?>
El resultado es una lista que contiene el elemento raíz actual y sus hijos (no los hijos de estos). Def: Elemento raíz := El elemento de menú de nivel superior que corresponde a la página actual o es padre de una página actual o padre de un padre...
Esto no responde exactamente a la pregunta original pero casi, ya que todavía está el elemento de nivel superior. Esto está bien para mí, porque quiero el elemento de nivel superior como título de la barra lateral. Si quieres deshacerte de esto, podrías tener que sobrescribir display_element o usar un analizador HTML.

Hola @jessegavin:
Los menús de navegación se almacenan en una combinación de tipos de entradas personalizadas y taxonomías personalizadas. Cada menú se guarda como un Término (ej. "Menú Acerca de", ubicado en wp_terms
) de una Taxonomía Personalizada (ej. nav_menu
, ubicada en wp_term_taxonomy
).
Cada elemento del menú de navegación se almacena como una entrada con post_type=='nav_menu_item'
(ej. "Acerca de la Empresa", ubicado en wp_posts
) con sus atributos guardados como metadatos (en wp_postmeta
) usando un prefijo meta_key
de _menu_item_*
donde _menu_item_menu_item_parent
es el ID de la entrada padre del elemento de menú.
La relación entre menús y elementos de menú se guarda en wp_term_relationships
donde object_id
se relaciona con $post->ID
del elemento de menú y $term_relationships->term_taxonomy_id
se relaciona con el menú definido colectivamente en wp_term_taxonomy
y wp_terms
.
Estoy bastante seguro de que sería posible enganchar tanto 'wp_update_nav_menu'
como 'wp_update_nav_menu_item'
para crear menús reales en wp_terms
y un conjunto paralelo de relaciones en wp_term_taxonomy
y wp_term_relationships
donde cada elemento de menú que tenga sub-elementos se convierta también en su propio menú de navegación.
También querrías enganchar 'wp_get_nav_menus'
(que sugerí que se añadiera a WP 3.0 basándome en un trabajo similar que hice hace unos meses) para asegurarte de que los menús generados no se muestren para su manipulación por el usuario en el admin, de lo contrario se desincronizarían muy rápido y tendrías una pesadilla de datos.
Suena como un proyecto divertido y útil, pero requiere un poco más de código y pruebas de las que puedo permitirme ahora, en parte porque cualquier cosa que sincronice datos tiende a ser un dolor de cabeza al depurar todos los errores (y porque los clientes que pagan me presionan para que termine otros trabajos. :) Pero con la información anterior, estoy seguro de que un desarrollador motivado de plugins para WordPress podría codificarlo si lo deseara.
¡Por supuesto, ahora que si lo codificas, estás obligado a publicarlo aquí para que todos podamos beneficiarnos de tu generosidad! :-)

No estoy seguro de estar siguiendo lo que dices. Estoy buscando una solución de solo lectura para mostrar "submenús" relacionados con la página actual en la que se encuentra un usuario. ¿Estamos hablando de lo mismo? - Aprecio mucho tu explicación detallada sobre el esquema de la base de datos.

@jessegavin - Sí, si quieres usar wp_nav_menu()
entonces necesitarás clonar los menús porque wp_nav_menu()
está estrechamente acoplado a la estructura del menú. La otra opción es copiar el código de wp_nav_menu()
y hacer las modificaciones necesarias para mostrarlo como un submenú.

Esta es una extensión del walker que debería hacer lo que estás buscando:
class Selective_Walker extends Walker_Nav_Menu
{
function walk( $elements, $max_depth) {
$args = array_slice(func_get_args(), 2);
$output = '';
if ($max_depth < -1) //parámetro inválido
return $output;
if (empty($elements)) //nada que recorrer
return $output;
$id_field = $this->db_fields['id'];
$parent_field = $this->db_fields['parent'];
// visualización plana
if ( -1 == $max_depth ) {
$empty_array = array();
foreach ( $elements as $e )
$this->display_element( $e, $empty_array, 1, 0, $args, $output );
return $output;
}
/*
* necesita mostrar en orden jerárquico
* separa elementos en dos grupos: nivel superior y elementos hijos
* children_elements es un array bidimensional, ej.
* children_elements[10][] contiene todos los sub-elementos cuyo padre es 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;
}
/*
* cuando ninguno de los elementos es de nivel superior
* asume que el primero debe ser la raíz de los sub-elementos
*/
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 ) {
// desciende solo en el árbol actual
$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 );
}
/*
* si estamos mostrando todos los niveles, y children_elements restante no está vacío,
* entonces tenemos huérfanos, que deben mostrarse de todos modos
*/
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;
}
}
Basado libremente en el código de mfields que mencioné en mi comentario anterior. Todo lo que hace es verificar al recorrer el menú si el elemento actual es (1) el elemento de menú actual, o (2) un ancestro del elemento de menú actual, y expande el subárbol debajo de él solo si alguna de esas condiciones es verdadera. Espero que te funcione.
Para usarlo, simplemente agrega un argumento "walker" cuando llames al menú, por ejemplo:
<?php wp_nav_menu(
array(
'theme_location'=>'test',
'walker'=>new Selective_Walker() )
); ?>

Oh... Acabo de releer tu pregunta y me di cuenta de que la había entendido mal al principio. Este walker mostrará todos los demás elementos del menú de primer nivel, pero no los expandirá. Esto no era exactamente lo que querías hacer. Aún así, este código se puede modificar como desees. Solo revisa el bucle a través de $top_level_elements
y añade tu propia prueba antes de la llamada a $this->display_element
.

¿Es posible hacer que esta clase muestre la profundidad de la subpágina actual? Es decir... Si tengo una profundidad de tres o más niveles, ¿que se muestren el tercer nivel y los siguientes para la (sub)página actual? En este momento, solo muestra A > B, pero no > C (siendo C el tercer nivel)

@Zolomon - No estoy seguro de entender tu pregunta. Esto debería expandir todo el árbol bajo cualquier elemento del menú con las clases 'current-menu-item', 'current-menu-parent' o 'current-menu-ancestor'. Cuando lo pruebo, muestra todos los niveles de subpáginas en el menú. ¿Qué es lo que estás intentando hacer?

Actualización: He convertido esto en un plugin. Descárgalo aquí.
Necesitaba resolver esto por mí mismo y terminé escribiendo un filtro sobre los resultados de la búsqueda del menú. Te permite usar wp_nav_menu
normalmente, pero seleccionar una sub-sección del menú basada en el título del elemento padre. Agrega un parámetro submenu
al menú de la siguiente manera:
wp_nav_menu(array(
'menu' => 'header',
'submenu' => 'Sobre Nosotros',
));
Incluso puedes ir varios niveles de profundidad usando barras:
wp_nav_menu(array(
'menu' => 'header',
'submenu' => 'Sobre Nosotros/Consejo Directivo'
));
O si prefieres con un array:
wp_nav_menu(array(
'menu' => 'header',
'submenu' => array('Sobre Nosotros', 'Consejo Directivo')
));
Utiliza una versión en slug del título, lo que lo hace tolerante a cosas como mayúsculas y puntuación.

¿Es posible acceder a un submenú mediante un id? Me refiero al id de página o al id de entrada.

split() está obsoleto, reemplaza $loc = split( "/", $loc );
en el plugin con $loc = preg_split( "~/~", $loc );

Armé la siguiente clase para mí mismo. Encontrará el elemento principal de navegación superior de la página actual, o puedes proporcionarle un ID de navegación superior objetivo en el constructor del walker.
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();
// si el padre no está establecido, configúralo basado en el post
if (!$target_id) {
global $post;
foreach ($items as $item) {
if ($item->object_id == $post->ID) {
$target_id = $item->ID;
}
}
}
// si no hay un padre, muestra un menú regular
if (!$target_id) return parent::walk($items, $depth, $args);
// obtén el elemento de navegación superior
$target_id = $this->top_level_id($items, $target_id);
// solo incluye elementos bajo el padre
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);
}
// obtiene el ID de nivel superior para un ID de elemento
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;
}
}
// encuentra el elemento de nivel superior
while (array_key_exists($item_id, $parents)) {
$item_id = $parents[$item_id];
}
return $item_id;
}
}
Llamada al menú de navegación:
wp_nav_menu(array(
'theme_location' => 'main_menu',
'walker' => new Walker_SubNav_Menu(22), // con ID
));

@davidn @hakre Hola, tengo una solución poco elegante sin un analizador HTML o sobrescribir 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) //parámetro inválido
return $output;
if (empty($elements)) //nada que recorrer
return $output;
$id_field = $this->db_fields['id'];
$parent_field = $this->db_fields['parent'];
// visualización plana
if ( -1 == $max_depth ) {
$empty_array = array();
foreach ( $elements as $e )
$this->display_element( $e, $empty_array, 1, 0, $args, $output );
return $output;
}
/*
* necesita mostrarse en orden jerárquico
* separar elementos en dos grupos: nivel superior y elementos hijos
* children_elements es un array bidimensional, ej.
* children_elements[10][] contiene todos los sub-elementos cuyo padre es 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;
}
/*
* cuando ninguno de los elementos es de nivel superior
* asumir que el primero debe ser la raíz de los sub-elementos
*/
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' ); //añadido por continent7
foreach ( $top_level_elements as $e ){ //modificado por continent7
// descender solo en el árbol actual
$descend_test = array_intersect( $current_element_markers, $e->classes );
if ( !empty( $descend_test ) )
$this->display_element( $e, $children_elements, 2, 0, $args, $output );
}
/*
* si estamos mostrando todos los niveles, y children_elements no está vacío,
* entonces tenemos huérfanos, que deben mostrarse de todos modos
*/
/* eliminado por 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 );
}
*/
/*añadido por 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;
}
}

Después de probarlos todos, la solución de Alp fue la única que funcionó para mí. Sin embargo, hay un problema con ella. Solo muestra los hijos de primer nivel, pero no muestra los hijos de tercer o cuarto nivel. He estado intentando durante días lograr que lo haga. ¿Alguien sabe cómo modificar su solución para que lo haga? PD: No me permite agregar comentarios, así que tuve que hacerlo como una respuesta.

El resultado del menú de navegación incluye muchas clases para el elemento actual, ancestro del elemento actual, etc. En algunas situaciones, he podido hacer lo que quieres hacer dejando que se genere todo el árbol de navegación y luego usando CSS para reducirlo solo a los hijos de la página actual, etc.

Hice un walker modificado que debería ayudar! No es perfecto - deja algunos elementos vacíos, pero hace el trabajo. La modificación son básicamente esas partes de $current_branch. ¡Espero que le sirva a alguien!
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 Pasado por referencia. Usado para añadir contenido adicional.
* @param int $depth Profundidad de página. Usado para 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 Pasado por referencia. Usado para añadir contenido adicional.
* @param int $depth Profundidad de página. Usado para 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 Pasado por referencia. Usado para añadir contenido adicional.
* @param object $item Objeto de datos del item de menú.
* @param int $depth Profundidad del item de menú. Usado para padding.
* @param int $current_page ID del item de menú.
* @param object $args
*/
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
global $current_branch;
// ¿Está este item de menú en la rama actual?
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 Pasado por referencia. Usado para añadir contenido adicional.
* @param object $item Objeto de datos de página. No usado.
* @param int $depth Profundidad de página. No usado.
*/
function end_el(&$output, $item, $depth) {
global $current_branch;
if($current_branch && $depth > 0) $output .= "</li>\n";
if($depth == 0) $current_branch = 0;
}
}

Echa un vistazo al código de mi plugin o utilízalo para tus propósitos ;)
Este plugin añade un widget mejorado de "Menú de Navegación". Ofrece muchas opciones que se pueden configurar para personalizar la salida del menú personalizado a través del widget.
Las características incluyen:
- Jerarquía personalizada - "Solo subelementos relacionados" o "Solo subelementos estrictamente relacionados".
- Profundidad inicial y nivel máximo a mostrar + visualización plana.
- Mostrar todos los elementos del menú comenzando por el seleccionado.
- Mostrar solo la ruta directa al elemento actual o solo los hijos del elemento seleccionado (opción para incluir el elemento padre).
- Clase personalizada para el bloque del widget.
- Y casi todos los parámetros para la función wp_nav_menu.

La respuesta aceptada reconoce que requiere entrada y no se basa en el contexto. Aquí hay una versión que admite que se le proporcione un contexto, o puede usar las clases integradas de WordPress current-x
para determinar el contexto:
add_filter( 'wp_nav_menu_objects', 'limit_tree', 10, 2 );
function limit_tree( $items, $args ) {
if ( empty( $args->context ) ) {
return $items;
}
if('current' == $args->context) {
$current_pages = array_filter($items, function($item) {
return !empty(array_intersect(['current-menu-parent', 'current-menu-ancestor', 'current-menu-item'], $item->classes));
});
$parent_id = array_pop($current_pages)->ID;
} else {
$ids = wp_filter_object_list( $items, array( 'object_id' => $args->context ), 'and', 'ID' );
$parent_id = array_pop( $ids );
}
$children = submenu_get_children_ids( $parent_id, $items );
foreach ( $items as $key => $item ) {
// Esto se puede ajustar si no deseas incluir el elemento padre
if ( $item->ID != $parent_id && ! 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;
}
Y su uso:
wp_nav_menu([
'theme_location' => 'foo-bar',
'context' => 'current',
]);
O...
wp_nav_menu([
'theme_location' => 'foo-bar',
'context' => get_queried_object_id(), // o algún ID de publicación arbitrario
]);
