How do I generate a custom menu/sub-menu system using wp_get_nav_menu_items in WordPress?

Viewed 101987

I have an html structure that requires customization of the wp_nav_menu code.

This is the html I need to generate:

<ul class="main-nav">
    <li class="item">
        <a href="http://example.com/?p=123" class="title">Title</a>
        <a href="http://example.com/?p=123" class="desc">Description</a>
        <ul class="sub-menu">
            <li class="item">
                <a href="http://example.com/?p=123" class="title">Title</a>
                <a href="http://example.com/?p=123" class="desc">Description</a>
            </li>
        </ul>
    </li>
     <li class="item">
        <a href="http://example.com/?p=123" class="title">Title</a>
        <a href="http://example.com/?p=123" class="desc">Description</a>
    </li>
</ul>

I am currently using wp_get_nav_menu_items to get all the items from my menu as an array.

Right now I am able to generate the above html without the sub-menus using the following code:

<?php

$menu_name = 'main-nav';
$locations = get_nav_menu_locations()
$menu = wp_get_nav_menu_object( $locations[ $menu_name ] );
$menuitems = wp_get_nav_menu_items( $menu->term_id, array( 'order' => 'DESC' ) );

foreach ( $menuitems as $item ):

    $id = get_post_meta( $item->ID, '_menu_item_object_id', true );
    $page = get_page( $id );
    $link = get_page_link( $id ); ?>

    <li class="item">
        <a href="<?php echo $link; ?>" class="title">
            <?php echo $page->post_title; ?>
        </a>
        <a href="<?php echo $link; ?>" class="desc">
            <?php echo $page->post_excerpt; ?>
        </a>
    </li>

<?php endforeach; ?>

I would have generated the menu using the wp_nav_menu function but I still need the description shown using $page->post_excerpt.

I've found that there is a property for each item called $item->menu_item_parent which gives the ID of the parent menu item.

How would I generate the sub-menu in my foreach loop? Or is there a really simple way using wp_nav_menu which Google forgot to mention?

6 Answers

Complete Code explanation.....

First Create two below two functions in functions.php file

 function get_menu_id( $location ){
                //get all the location....
                $locations =get_nav_menu_locations();
    
                //get object id by location.....
                $menu_id = $locations[$location];
                
                return !empty($menu_id) ? $menu_id : '';
            }


            function get_child_menu_items($menu_array , $parent_id){
                $child_menu = [];
                if(!empty($menu_array) && is_array($menu_array)){
                    foreach($menu_array as $menu){
                        if( intval($menu->menu_item_parent) === $parent_id){
                            array_push($child_menu , $menu);
                        }
                    }
                }
                return $child_menu;
            }

then

$menu_name = 'menu_name';
                $location = get_nav_menu_locations();
                $menu = wp_get_nav_menu_object($location[$menu_name]);
                $menuitems = wp_get_nav_menu_items( $menu->term_id);
                $header_menu_id = get_menu_id('primary-menu');
                
                $header_menus = wp_get_nav_menu_items($header_menu_id);

now this is my html and php code....

<div class="collapse navbar-collapse" id="navbarSupportedContent">


                    <?php
                        if(!empty($header_menus)  && is_array($header_menus)){
                            ?>
                            <ul class="navbar-nav ml-auto">
                                <?php
                                    foreach($header_menus as $menu_item){
                                        if(!$menu_item->menu_item_parent){ //checking the item are parent?
                                            $child_menu_items = get_child_menu_items($header_menus, $menu_item->ID);
                                            $has_children = !empty($child_menu_items) && is_array($child_menu_items);

                                            if(! $has_children){
                                                ?>
                                                <li class="nav-item">
                                                    <a class="nav-link" href="<?php echo esc_url($menu_item -> url); ?>"> <?php echo esc_html($menu_item->title); ?></a> 
                                                </li>
                                                <?php
                                            }else{
                                                ?>
                                            <li class="nav-item dropdown">
                                                <a class="nav-link dropdown-toggle" href="<?php echo esc_url($menu_item -> url); ?>" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                                <?php echo esc_html($menu_item->title); ?>
                                                </a>
                                                <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                                                    <?php
                                                        foreach($child_menu_items as $child){
                                                            ?>
                                                            <a class="dropdown-item" href="<?php echo esc_url($child->url); ?>" > <?php echo esc_html($child->title); ?></a> 
                                                            <?php
                                                        }
                                                    ?>        
                                                </div>
                                            </li>
                                                <?php
                                            }
                                        }
                                    }
                                ?>
                            </ul>
                            <?php
                        }
                    ?>

Pleae try this. It works for me.

<?php
        $menuLocations = get_nav_menu_locations();
        $menuID = $menuLocations['primary'];
        $menu = wp_get_nav_menu_object($menuLocations['primary']);
        $menuitems = wp_get_nav_menu_items($menu->term_id, array('order' => 'DESC'));
        $ParentArray = array();
        $count = 0;
        $submenu = false;

    foreach ($menuitems as $menu_item) {
        $link = $menu_item->url;
        $title = $menu_item->title;
        if ($menu_item->menu_item_parent == 0   ) {
            $parent_id = $menu_item->ID;
            echo  '<li class="main-li dropmenu"><a href="' .$menu_item->url . '">'.$menu_item->title .'</a>';
        }
        if ($parent_id == $menu_item->menu_item_parent ) {
            if (!$submenu ) {
                $submenu = true;
                echo '<ul class="level1">';
            }
             echo '<li><a href="'.$link.'">'.$title.'</a></li>';
            if ($menuitems[ $count + 1 ]->menu_item_parent != $parent_id && $submenu ) {
                echo '</ul>';
                $submenu = false;
            }
        }
        if ($menuitems[ $count + 1 ]->menu_item_parent != $parent_id ) {
            echo '</li>';
            $submenu = false;
        }
        $count++;
    }
    ?>

If your theme uses has_nav_menu('<menu-name>') in the header or footer.php(for example), you can go to Appearance > Menus and select which menu or sub-menu to configure.

Related