我在WP Admin中定义了一个菜单,如下所示:



我希望每当我在父母身边时,都能够在侧栏上显示所有子链接。页。例如,如果用户在我的“关于我们”页面上,我希望在侧面栏中显示以绿色突出显示的4个链接的列表。

我查看了wp_nav_menu()和它似乎没有任何内置方法来指定给定菜单的特定节点以用作生成链接时的起点。

我为类似情况创建了解决方案,关于由页面父级创建的关系,但是我正在寻找一种专门使用菜单系统的关系。任何帮助将不胜感激。

评论

因此,您想将整个菜单都保留为自定义菜单,但是要创建一个自定义的行进器,以仅扩展活动子树来显示它?喜欢这段代码,但是扩展了wp_nav_menu而不是wp_list_pages?我最近做了类似的事情,并且如果您正在寻找它,可以发布代码...

@goldenapples,那正是我所追求的。如果您不介意将代码作为答案发布,我将不胜感激。

我不知道这样明显的有用功能是否尚未构建。对于所有执行“ CMS”的网站来说,这总体来说非常有用。

我正在尝试解决上述问题或类似问题。作为替代方案,我在这里提出了CSS解决方案:stackoverflow.com/q/7640837/518169

#1 楼

这仍然在我的脑海中,因此我重新审视并组合了这种解决方案,该解决方案并不太依赖上下文:

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


评论


可爱的技术!请问与此相关的一些问题:如何在模板中显示列出的子菜单页面的内容?

– daniel.tosaba
2012年8月22日5:09

@ daniel.tosaba,您需要在Walker_Nav_Menu类中子类化或使用过滤器。像所有菜单一样,太多东西无法发表评论-问新问题吗?

–稀有
2012年8月22日11:18



wordpress.stackexchange.com/questions/62758/…

– daniel.tosaba
2012年8月23日7:06

如此奇妙的答案。非常感谢。这实际上应该是WordPress中的默认选项。

–点点
2012年9月11日上午11:19

真的很整洁。如果有人感兴趣,可以通过页面ID进行相同操作,将wp_filter_object_list行更改为wp_filter_object_list($ items,array('object_id'=> $ args-> submenu),'and','ID');

–本
15年4月30日在10:25

#2 楼

@goldenapples:您的Walker课堂无效。但是这个主意真的很好。我根据您的想法创建了助行器:

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


现在您可以使用:

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


输出是包含当前根元素及其子项(而不是其子项)的列表。 Def:根元素:=与当前页面相对应或当前页面的父级或父级父级的顶级菜单项...

这不能完全回答原始问题,但差不多,因为仍然有顶层项目。这对我来说很好,因为我希望将顶层元素用作侧边栏的标题。如果要摆脱这种情况,则可能必须重写display_element或使用HTML解析器。

#3 楼

@jessegavin,您好:

导航菜单存储在自定义帖子类型和自定义分类法的组合中。每个菜单都存储为自定义分类法(即wp_termsnav_menu)的术语(即wp_term_taxonomy中的“关于菜单”)。每个导航菜单项都作为post_type=='nav_menu_item'的帖子存储(即在wp_posts中找到的“关于公司”),其属性使用wp_postmetameta_key前缀存储在post meta中(在_menu_item_*中),其中_menu_item_menu_item_parent是菜单项的父级导航菜单项post的ID。

菜单和菜单项之间的关系存储在wp_term_relationships中,其中object_id与Nav菜单项的$post->ID相关,而$term_relationships->term_taxonomy_id与在wp_term_taxonomywp_terms中共同定义的菜单相关。

我很漂亮确保可以同时挂接'wp_update_nav_menu''wp_update_nav_menu_item'来在wp_terms中创建实际菜单,并在wp_term_taxonomywp_term_relationships中创建一组平行关系,其中每个具有子导航菜单项的导航菜单项也将变为其自己的导航菜单。

您还希望钩住'wp_get_nav_menus'(我根据几个月前所做的一些类似工作,建议将其添加到WP 3.0中),以确保不会显示您生成的导航菜单进行操作由管理员中的用户执行,否则他们将很快变得不同步,然后您将面临数据噩梦。

听起来像一个有趣而有用的项目,但这是一个比我现在所能解决的要多的代码和测试,部分是因为在消除所有错误时,任何用于同步数据的数据都趋向于成为PITA(并且由于付费客户正要求我把事情做好。:)但是有了上述信息,我相当有动力,如果他们愿意,WordPress插件开发人员可以对其进行编码。这样我们就能从您的慷慨中受益! :-)

评论


我不确定我是否会遵循您的意思。我正在寻找一种只读解决方案,用于显示与用户当前页面相关的“子菜单”。我们在谈论同一件事吗? -非常感谢您对数据库架构的更深入的说明。

– jessegavin
10-10-14在1:24

@jessegavin-是的,如果要调用wp_nav_menu(),则需要克隆菜单,因为wp_nav_menu()与菜单结构紧密相关。另一种选择是复制wp_nav_menu()代码并进行必要的修改以显示为子菜单。

– MikeSchinkel
2010-10-14 2:34



这是我一直在寻找的答案,非常感谢!

–克里斯·哈斯(Chris Haas)
19/12/13在20:39

#4 楼

这是一个Walker扩展程序,应该可以满足您的需求:它所做的只是在遍历菜单时检查是否当前元素是(1)当前菜单项,或(2)当前菜单项的祖先,并且仅当以下任一条件为真时,才展开其下方的子树。希望这对您有用。

要使用它,只需在调用菜单时添加一个“ walker”参数,即:

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

}


评论


哦,我只是重新阅读了您的问题,才意识到我一开始误解了。该walker将显示所有其他顶级菜单项,只是不展开它们。这不完全是您想要的。不过,可以按照您想要的任何方式修改此代码。只需查看$ top_level_elements中的循环并在调用$ this-> display_element之前添加自己的测试即可。

– Goldenapples
2010-10-16 19:46

是否可以让此类显示当前子页面的深度?那就是..如果我的深度达到三个或三个以上级别,那么当前(子)页面会显示第三和后续级别吗?目前,它仅显示A> B,而不显示> C(C是第三个(级别)

–所罗门
2011-2-20在20:10

@Zolomon-我不确定我是否理解您的问题。这应该在带有“当前菜单项”,“当前菜单父项”或“当前菜单祖先”类的任何菜单项下扩展整个树。当我对其进行测试时,它会在菜单中显示所有子页面级别。您想做什么?

– Goldenapples
2011年2月21日在20:39

也许您想将深度参数传递给对wp_nav_menu的调用,以防您的主题以某种方式覆盖默认值0(显示所有级别)?

– Goldenapples
2011年2月21日在20:41

#5 楼

更新:我把它变成了一个插件。在这里下载。


我需要自己解决这个问题,最终为菜单查找结果写一个过滤器。它使您可以照常使用wp_nav_menu,但可以根据父元素的标题选择菜单的子部分。像这样在菜单中添加一个submenu参数:

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


通过在其中添加斜杠,甚至可以深入几个层次:

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


,或者如果您希望使用数组:标点符号。

评论


是否可以通过ID进入子菜单?我的意思是页面ID或帖子ID。

– Digerkam
13年2月28日在16:28

split()已被弃用,替换$ loc = split(“ /”,$ loc);在具有$ loc = preg_split(“〜/〜”,$ loc)的插件中;

–花
18年9月4日在10:29

我还建议将$ submenu设为可选。因此,您仍然可以在需要时获取整个菜单。将其添加到过滤器顶部:`if(!isset($ args-> submenu)){return $ items; }`

–花
18-09-4在10:43

#6 楼

我为自己安排了以下课程。它会找到当前页面的顶级导航父级,或者您可以在walker构造函数中为其指定目标顶级导航ID。

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


导航调用:

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


#7 楼

@davidn @hakre
嗨,我有一个丑陋的解决方案,没有HTML解析器或覆盖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;
        }
    }


评论


在尝试了所有方法之后,Alp的解决方案是唯一对我有用的解决方案。但是,这是一个问题。它仅显示第一级的孩子,而不显示第三级或第四级的孩子。我已经尝试了好几天才能做到这一点。任何人都知道如何修改自己的解决方案吗? PS。它不会让我添加评论,因此需要做为答案。

–cchiera
2011年5月24日在1:30

#8 楼

导航菜单输出包括当前项目,当前项目祖先等的许多类。在某些情况下,我可以通过让整个导航树输出然后使用css将其缩减为以下内容来完成您想做的事情仅当前页面的子级,等等。

#9 楼

我做了一个改良的助行器,应该有所帮助!并不完美-它留下了一些空元素,但是可以解决问题。修改基本上是那些$ current_branch位。
希望对您有所帮助!

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


}

#10 楼

在我的插件中查看代码或将其用于您的目的;)

此插件添加了增强的“导航菜单”小部件。它提供了许多选项,可以通过小部件将其设置为自定义自定义菜单的输出。

功能包括:


自定义层次结构-“仅相关子-项”或“仅严格相关的
子项”。
开始显示的深度和最大水平+平面显示。
显示从所选项开始的所有菜单项。
仅显示指向当前元素的直接路径,或仅显示
所选项目的子项(包含父项的选项)。
小部件块的自定义类。
以及wp_nav_menu函数的几乎所有参数。

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