it-swarm.dev

Mostra una porzione/ramo dell'albero dei menu usando wp_nav_menu ()

Ho un menu definito in WP Admin che assomiglia a questo:

alt text

Voglio essere in grado di visualizzare tutti i collegamenti figlio nella barra laterale ogni volta che mi trovo in una pagina padre. Ad esempio, se l'utente è sulla mia pagina "Chi siamo", voglio che un elenco dei 4 link evidenziati in verde appaia nella barra laterale.

Ho esaminato la documentazione di wp_nav_menu () e non sembra che abbia alcun modo incorporato per specificare un particolare nodo di un determinato menu da utilizzare come punto di partenza durante la generazione dei collegamenti.

Ho creato una soluzione per una situazione simile che si basava sulle relazioni create dal padre della pagina, ma ne sto cercando una che utilizzi specificamente il sistema di menu. Qualsiasi aiuto sarebbe apprezzato.

109
jessegavin

Questo era ancora nella mia mente, quindi l'ho rivisitato e ho messo insieme questa soluzione, che non si basa molto sul contesto:

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-of-the-menu', // the one used on register_nav_menus
    'submenu' => 'About Us', // could be used __() for translations
);

wp_nav_menu( $args );
73
Rarst

@goldenapples: La tua classe Walker non funziona. Ma l'idea è davvero buona. Ho creato un camminatore basato sulla tua 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) //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;
    }
}

Ora puoi usare:

<?php wp_nav_menu( 
   array(
       'theme_location'=>'test', 
       'walker'=>new Selective_Walker() ) 
   ); ?>

L'output è una lista contenente l'elemento radice corrente e sono i figli (non i loro figli). Def: Root element: = La voce di menu di livello superiore che corrisponde alla pagina corrente o è padre di una pagina corrente o di un genitore di un genitore ...

Questo non risponde esattamente alla domanda originale, ma quasi, poiché c'è ancora l'elemento di livello superiore. Questo va bene per me, perché voglio l'elemento di livello superiore come titolo della barra laterale. Se vuoi liberarti di questo, potresti dover sovrascrivere display_element o usare un parser HTML.

14
davidn

Ciao @jessegavin :

I menu di navigazione sono memorizzati in una combinazione di tipi di post personalizzati e tassonomie personalizzate. Ogni menu è memorizzato come Termine (cioè "Informazioni sul Menu", trovato in wp_terms) di una Tassonomia Personalizzata (cioè nav_menu, che si trova in wp_term_taxonomy.)

Ogni Nav Menu Item è memorizzato come un post di post_type=='nav_menu_item' (cioè "Informazioni sull'azienda", trovato in wp_posts) con gli attributi memorizzati come post meta (in wp_postmeta) usando un prefisso meta_key di _menu_item_* dove _menu_item_menu_item_parent è il ID della voce di menu principale del menu Nav dell'oggetto della voce di menu.

La relazione tra menu e voci di menu è memorizzata in wp_term_relationships dove object_id si riferisce al $post->ID per la voce di menu Nav e $term_relationships->term_taxonomy_id si riferisce al menu definito collettivamente in wp_term_taxonomyand wp_terms.

Sono abbastanza sicuro che sarebbe possibile hook sia 'wp_update_nav_menu' che 'wp_update_nav_menu_item' per creare menu effettivi in ​​wp_terms e un insieme parallelo di relazioni in wp_term_taxonomy e wp_term_relationships in cui ogni voce di menu Nav che ha voci di menu sub-Nav anche diventa il proprio menu di navigazione.

Dovresti anche hook'wp_get_nav_menus'(che ho suggerito di aggiungere a WP 3.0 basato su un lavoro simile che stavo facendo alcuni mesi fa) per assicurarti che i tuoi menu di navigazione generati non vengano visualizzati per la manipolazione dall'utente nell'amministratore, altrimenti finirebbero fuori sincrono molto velocemente e quindi avresti un incubo di dati nella tua mano.

Sembra un progetto divertente e utile, ma è un po 'più di codice e testing di quanto possa permettermi di affrontare in questo momento in parte perché tutto ciò che sincronizza i dati tende ad essere un PITA quando si tratta di stirare tutti i bug (e perché i clienti paganti mi premono per fare le cose :) :} Ma armati delle informazioni di cui sopra sono un motivato sviluppatore di plugin per WordPress che potrebbe codificarlo se lo volessero.

Naturalmente ora ti rendi conto che se lo decidi, sei obbligato a postarlo di nuovo in modo che tutti possano beneficiare della tua generosità! :-)

12
MikeSchinkel

Questa è un'estensione Walker che dovrebbe fare ciò che stai cercando:

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;
    }

}

Basato in modo approssimativo sul codice di mfields ho fatto riferimento nel mio commento in precedenza. Tutto ciò che fa è controllare quando si cammina nel menu per vedere se l'elemento corrente è (1) la voce di menu corrente, o (2) un antenato della voce di menu corrente, e espande la sottostruttura sottostante solo se una di queste condizioni è vera . Spero che questo funzioni per te.

Per usarlo, basta aggiungere un argomento "walker" quando si chiama il menu, cioè:

<?php wp_nav_menu( 
   array(
       'theme_location'=>'test', 
       'walker'=>new Selective_Walker() ) 
   ); ?>
10
goldenapples

Ho messo insieme la seguente lezione per me stesso. Troverà il top gen superiore della pagina corrente, oppure puoi assegnargli un ID nav top target nel costruttore 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();

        // 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;
    }
}

Chiamata nav:

wp_nav_menu(array(
    'theme_location' => 'main_menu',
    'walker' => new Walker_SubNav_Menu(22), // with ID
));
8
Matt

Aggiornamento: Ho fatto questo in un plugin. Scarica qui .


Avevo bisogno di risolverlo da solo e alla fine ho finito col scrivere un filtro sui risultati della ricerca del menu. Ti consente di usare wp_nav_menu normalmente, ma scegli una sottosezione del menu in base al titolo dell'elemento genitore. Aggiungi un parametro submenu al menu in questo modo:

wp_nav_menu(array(
  'menu' => 'header',
  'submenu' => 'About Us',
));

Puoi persino scendere a diversi livelli mettendo le barre in:

wp_nav_menu(array(
  'menu' => 'header',
  'submenu' => 'About Us/Board of Directors'
));

O se preferisci con un array:

wp_nav_menu(array(
  'menu' => 'header',
  'submenu' => array('About Us', 'Board of Directors')
));

Utilizza una versione slug del titolo, che dovrebbe renderlo indulgente a cose come maiuscole e punteggiatura.

8
Marcus Downing

@davidn @hakre Ciao, ho una brutta soluzione senza un parser HTML o sovrascrivendo 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;
        }
    }
4
Alp Güneysel

Controlla il codice nel mio plugin o usalo per il tuo scopo;)

Questo plugin aggiunge il widget "Menu di navigazione" avanzato. Offre molte opzioni che potrebbero essere impostate per personalizzare l'output del menu personalizzato attraverso il widget.

Le caratteristiche includono:

  • Gerarchia personalizzata - "Solo voci secondarie correlate" o "Solo voci secondarie strettamente correlate".
  • Profondità iniziale e livello massimo per visualizzare + display piatto.
  • Mostra tutte le voci di menu che iniziano con quella selezionata.
  • Mostra solo il percorso diretto verso l'elemento corrente o solo i bambini di
    elemento selezionato (opzione per includere l'elemento principale).
  • Classe personalizzata per un blocco di widget.
  • E quasi tutti i parametri per la funzione wp_nav_menu.

http://wordpress.org/extend/plugins/advanced-menu-widget/

3
Ján Bočínec

Ho fatto un walker modificato che dovrebbe aiutare! Non perfetto - lascia alcuni elementi vuoti, ma fa il trucco. La modifica è fondamentalmente quei bit $ current_branch. Spero che aiuti qualcuno!

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;
}

}

3
user2735

L'output del menu di navigazione include molte classi per l'elemento corrente, l'antenato corrente dell'articolo, ecc. In alcune situazioni, sono stato in grado di fare ciò che si desidera fare lasciando l'output dell'intero albero di navigazione, e quindi usare css per ridurlo a solo i bambini della pagina corrente, ecc.

3
user3017