使用wp_NAV_MENU动态生成子菜单

时间:2013-06-25 作者:danbrellis

目标:使用wordpress的wp\\u nav\\u菜单生成一个菜单,其中动态添加子页面,同时保留正确的子菜单类。

Before:

<安装习惯

After:

<安装屋顶花园习惯基本上,我有一个wp\\U nav\\U菜单来显示一些页面(即“安装”和“习惯”)。其中任何一个页面都可能有多个子页面与之关联,我希望我的导航能够动态地将这些子页面添加到菜单中,而不使用wp\\u list\\u页面。在我的例子中,nav是从一个自定义的post类型(“actions”)中提取的,但这可以自定义。

2 个回复
最合适的回答,由SO网友:danbrellis 整理而成

我的解决方案使用Walker\\u Nav\\u菜单和一个自定义类,主要是从以下帖子中拼凑而成:How do I dynamically populate wp_nav_menu from a custom taxonomy?

解决方案:

//define the custom post type
//could use "page" or "post" as well.
define("MENU_CPT", "action");

//custom function for selecting posts based on a page parent (ne\' term_id)
function hgs_get_children_by_id($parent_id, $post_type=MENU_CPT) {
    $args = array(
        \'posts_per_page\' => -1,
        \'post_type\' => $post_type,
        \'post_parent\' => $parent_id,
        \'orderby\' => \'menu_order title\',
        \'order\'  => \'DESC\'
    );                  
    return get_posts( $args );
}

//custom nav menu walker class for Take Action Dropdown
class HGS_Walker_Nav_Menu extends Walker_Nav_Menu {
    /**
     * Display array of elements hierarchically.
     *
     * It is a generic function which does not assume any existing order of
     * elements. max_depth = -1 means flatly display every element. max_depth =
     * 0 means display all levels. max_depth > 0  specifies the number of
     * display levels.
     *
     * @since 2.1.0
     *
     * @param array $elements
     * @param int $max_depth
     * @return string
     */
    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 ) 
            {
            //var_dump($e); //for troubleshooting
                $top_level_elements[] = $e;
                if ( $e->type==\'post_type\' && $e->object == MENU_CPT ) {

                    $child_posts = hgs_get_children_by_id($e->object_id);

                    foreach ( $child_posts as $child ) {                      
                        $child = wp_setup_nav_menu_item($child);
                        $child->post_type = \'nav_menu_item\';
                        $child->menu_item_parent = $e->$id_field;
                        $child->object = \'custom\';
                        $child->type = \'custom\';
                        $child->ID = $e->$id_field.$child->ID;
                        $children_elements[ $e->$id_field ][] = $child; 
                        $children_elements_classes[] = $child; 
                    }
                }
            }
            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;
                    if ( $e->type==\'post_type\' && $e->object == MENU_CPT ) {

                        $child_posts = hgs_get_children_by_id($e->object_id);

                        foreach ( $child_posts as $child ) {                      
                            $child = wp_setup_nav_menu_item($child);
                            $child->post_type = \'nav_menu_item\';
                            $child->menu_item_parent = $e->$id_field;
                            $child->object = \'custom\';
                            $child->type = \'custom\';
                            $child->ID = $e->$id_field.$child->ID;
                            $children_elements[ $e->$id_field ][] = $child;
                            $children_elements_classes[] = $child; 
                        }
                    }
                }
                else
                {
                    $children_elements[ $e->$parent_field ][] = $e;
                }
            }
        }

        //assing the classes to our dynamically populated posts
        if ( $children_elements_classes )
            _wp_menu_item_classes_by_context($children_elements_classes);

        foreach ( $top_level_elements as $e )
            $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;
    }
}
我们实现如下菜单:

<?php wp_nav_menu( array(
                        \'theme_location\'  => \'nav-right\',
                        \'menu\'            => \'Right Navigation Column\',
                        \'container\'       => \'div\',
                        \'container_class\' => \'nav-inner\',
                        \'container_id\'    => \'\',
                        \'menu_class\'      => \'menu\',
                        \'menu_id\'         => \'\',
                        \'echo\'            => true,
                        \'items_wrap\'      => \'<h2>Take Action</h2><ul class="%2$s">%3$s</ul>\',
                        **\'walker\'        => new HGS_Walker_Nav_Menu**
                    ) ); ?>
所以我希望这能帮助别人。如果您在一个菜单中使用各种帖子类型,并且在$e->type和$e->object上运行一个条件,那么它当然是非常可定制的。

如果有人对如何改进这一点有更多的见解或想法,请与我们分享。

SO网友:Jonas Lundman

如果您的目标是将页面自动添加到导航菜单,请使用如下类。

您可能想在更新页面或删除页面时添加一些内容,但这不是问题,只需添加即可。

class EntexAutoSubmenu {

    /**
     * Constructor
     */
    function __construct(){
        add_action(\'publish_page\', array($this, \'on_publish_page\'));
    }

    /**
     * When publishing a new child page, add it to the appropriate custom menu.
     */
    function on_publish_page($post_id){

        // Theme supports custom menus?
        if (!current_theme_supports(\'menus\')) return;

        // Published page has parent?
        $post = get_post($post_id);
        if (!$post->post_parent) return;

        $all_menus = get_terms(\'nav_menu\', array(\'hide_empty\' => true));

        // Loop through the menus to find page parent
        foreach ($all_menus as $menu) {

            $menu_parent = NULL;
            $menu_items = wp_get_nav_menu_items($menu->term_id, array(\'post_status\' => \'publish,draft\'));

            if (!is_array($menu_items)) continue;

            foreach ($menu_items as $menu_item){
                // Item already in menu?
                if ($menu_item->object_id == $post->ID) continue 2;
                if ($menu_item->object_id == $post->post_parent) $menu_parent = $menu_item;
            }

            // Add new item
            if ($menu_parent) {
                wp_update_nav_menu_item($menu->term_id, 0, array(
                    \'menu-item-object-id\' => $post->ID,
                    \'menu-item-object\' => $post->post_type,
                    \'menu-item-parent-id\' => $menu_parent->ID,
                    \'menu-item-type\' => \'post_type\',
                    \'menu-item-status\' => \'publish\'
                ));
            }
        }
    }
}

$auto_submenu = new EntexAutoSubmenu();

结束

相关推荐

涉及AJAX的自定义Walker导航问题

我在一个不是我写的主题中使用ajax。这个ajax点击链接,然后将该页面滑入视图。它工作得很好。为了做到这一点,我必须为wp\\U nav\\U菜单定制一个walker,在链接中添加两个字符,以便我的链接在nav中看起来像这样 http://my-site.com/#!/post-1 我的问题是,当查看单个页面时。我必须让WordPress重新加载页面才能获得单个帖子。那么我的链接会是这样的 http://my-site.com/?p=post-1 因此,当我将鼠标悬停在导航上以返回到