it-swarm.dev

Zobrazení částí/vět stromu stromu pomocí wp_nav_menu ()

V nabídce WP Správce, který vypadá takto:

alt text

Chci být schopen zobrazit všechny podřízené odkazy na postranním panelu vždy, když jsem na rodičovské stránce. Pokud je například uživatel na stránce "O nás", chci, aby se na bočním panelu zobrazil seznam 4 odkazů zvýrazněných zeleně.

Podíval jsem se na dokumentaci pro wp_nav_menu () a zdá se, že nemá žádný vestavěný způsob, jak určit konkrétní uzel dané nabídky, který bude použit jako výchozí bod pro generování odkazů.

Vytvořil jsem řešení pro podobnou situaci které se opíralo o vztahy vytvořené rodičem stránky, ale hledám ten, který používá systém menu konkrétně. Jakákoli pomoc by byla oceněna.

109
jessegavin

To bylo stále v mé mysli, tak jsem to znovu prohlédl a dal dohromady toto řešení, které se nespoléhá na kontext, který je tak moc:

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

Používání

$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: Vaše Walker Class nefunguje. Ale ten nápad je opravdu dobrý. Vytvořil jsem chodce na základě vaší myšlenky:

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

Nyní můžete použít:

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

Výstup je seznam obsahující aktuální kořenový prvek a jeho děti (ne jejich děti). Def: Root element: = Položka nabídky nejvyšší úrovně, která odpovídá aktuální stránce nebo je nadřazena aktuální stránce nebo nadřazenému nadřazenému ...

To neodpovídá přesně na původní otázku, ale téměř, protože stále existuje položka nejvyšší úrovně. To je pro mě v pořádku, protože chci prvek nejvyšší úrovně jako titulek postranního panelu. Pokud se toho chcete zbavit, budete možná muset přepsat display_element nebo použít HTML-Parser.

14
davidn

Dobrý den,@jessegavin:

Menu Nav jsou uloženy v kombinaci vlastních typů příspěvků a vlastních taxonomií. Každé menu je uloženo jako Termín (tj. "O Menu" , nalezené v wp_terms) vlastní taxonomie (tj. nav_menu, nalezené v wp_term_taxonomy.)

Každá položka menu Nav je uložena jako příspěvek post_type=='nav_menu_item' (tj. "O firmě" , nalezené v souboru wp_posts) s jejími atributy uloženými jako pošta meta (v wp_postmeta) pomocí meta_key předpony _menu_item_* kde _menu_item_menu_item_parent je ID položky nadřazené položky menu v menu.

Vztah mezi nabídkami a položkami menu je uložen v wp_term_relationships, kde object_id se vztahuje k $post->ID pro položku Nav Menu a $term_relationships->term_taxonomy_id se vztahuje na menu definované společně v wp_term_taxonomyand wp_terms.

Jsem si docela jistý, že by bylo možné hook oba 'wp_update_nav_menu' a 'wp_update_nav_menu_item' vytvořit aktuální menu v wp_terms a paralelní sadu vztahů v wp_term_taxonomy a wp_term_relationships, kde každá položka menu Nav, která má také položky dílčí nabídky Nav. je to vlastní menu Nav.

Také byste chtěli hook 'wp_get_nav_menus' (který jsem navrhl, aby byl přidán do WP 3.0 na základě podobné práce, kterou jsem dělal před několika měsíci) že vaše vygenerované Nav menu nejsou zobrazeny pro manipulaci uživatelem v admin, jinak by se dostali ze synchronizace opravdu rychle a pak byste měli mít na ruce data noční můru.

Zní to jako zábavný a užitečný projekt, ale je to o něco více kódu a testování, než si mohu dovolit řešit právě teď částečně, protože cokoliv, co synchronizuje data, bývá PITA, pokud jde o žehlení všech chyb (a proto, že platící klienti mě naléhají, abych to udělal. :) Ale vyzbrojeni výše uvedenými informacemi jsem docela motivovaný WordPress plugin vývojář by mohl kód, kdyby chtěli.

Samozřejmě si uvědomíte, že pokud to uděláte kód, jste povinni jej sem poslat zpět, abychom mohli těžit z vaší laskavosti! :-)

12
MikeSchinkel

Toto je rozšíření pro chodce, které by mělo dělat to, co hledáte:

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

}

Založen volně na kódu mfields 'jsem odkazoval v mém komentáři dříve. Jediné, co děláte, je zkontrolovat při procházení menu, zda je aktuální prvek (1) aktuální položkou nabídky, nebo (2) předchůdce aktuální položky nabídky, a rozšiřuje podstrom pod ní, pouze pokud je některá z těchto podmínek pravdivá. . Doufám, že to funguje pro vás.

Chcete-li jej použít, přidejte při volání nabídky argument „walker“, tj.:

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

Sestavil jsem pro sebe následující třídu. Nalezne horní nav rodič aktuální stránky, nebo mu můžete dát cíl top nav ID v konstruktoru 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;
    }
}

Hovor Nav:

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

Aktualizace: Udělal jsem to v pluginu. Stáhnout zde .


Potřeboval jsem to vyřešit sám a nakonec jsem zlikvidoval psaní filtru na výsledky vyhledávání v menu. To vám umožní používat wp_nav_menu jako normální, ale zvolit dílčí sekci nabídky na základě názvu nadřazeného prvku. Přidejte do nabídky parametr submenu:

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

Můžete dokonce jít několik úrovní hluboko tím, že lomítka v:

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

Nebo pokud dáváte přednost poli:

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

Používá slimovou verzi názvu, která by mu měla odpouštět věci jako kapitály a interpunkci.

8
Marcus Downing

@davidn @hakre Ahoj, já jsem ošklivé řešení bez HTML-Parser nebo přepsání 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

Podívejte se na kód v mém pluginu nebo ho použijte pro svůj účel;)

Tento plugin přidává vylepšený widget "Navigační menu". Nabízí mnoho možností, které mohou být nastaveny tak, aby přizpůsobily výstup uživatelského menu pomocí widgetu.

Mezi funkce patří:

  • Vlastní hierarchie - "Pouze související dílčí položky" nebo "Pouze přísně související dílčí položky".
  • Počáteční hloubka a maximální úroveň zobrazení + plochý displej.
  • Zobrazte všechny položky nabídky počínaje vybranou položkou.
  • Zobrazit pouze přímou cestu k aktuálnímu prvku nebo pouze dětem
    vybraná položka (možnost zahrnout nadřazenou položku).
  • Vlastní třída pro blok widgetů.
  • A téměř všechny parametry pro funkci wp_nav_menu.

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

3
Ján Bočínec

Udělal jsem upravený chodec, který by měl pomoci! Není dokonalý - zanechává několik prázdných prvků, ale dělá to trik. Modifikace je v podstatě těch bitů $ current_branch. Doufám, že to někomu pomůže!

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

Výstup menu obsahuje spoustu tříd pro aktuální položku, předchůdce aktuální položky, atd. V některých situacích jsem byl schopen udělat to, co chcete udělat, tím, že necháte celý výstup stromu stromů, a pak pomocí css pare to dolů na pouze děti z aktuální stránky, atd.

3
user3017