How to Generate a Custom Menu/Sub-Menu System Using Wp_Get_Nav_Menu_Items in Wordpress

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

For anyone who tackles something similar here's my solution:

Quick code example on a gist

Here's the code on a github gist for anyone who wants to get in on the copy paste action.

TL;DR

TL;DR Loop over list, drill down if there's a sub menu, close if we reach the end of the sub menu and menu.

Complete Code explanation

Firstly get the menu items as a flat array:

<?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' ) );
?>

Then iterate over the array of the menu items:

<nav>
<ul class="main-nav">
<?php
$count = 0;
$submenu = false;

foreach( $menuitems as $item ):
// set up title and url
$title = $item->title;
$link = $item->url;

// item does not have a parent so menu_item_parent equals 0 (false)
if ( !$item->menu_item_parent ):

// save this id for later comparison with sub-menu items
$parent_id = $item->ID;
?>

Write the first parent item <li>:

    <li class="item">
<a href="<?php echo $link; ?>" class="title">
<?php echo $title; ?>
</a>
<?php endif; ?>

Check that this items' parent id matches the stored parent id:

        <?php if ( $parent_id == $item->menu_item_parent ): ?>

Start sub-menu <ul> and set $submenu flag to true for later referance:

            <?php if ( !$submenu ): $submenu = true; ?>
<ul class="sub-menu">
<?php endif; ?>

Write the sub-menu item:

                <li class="item">
<a href="<?php echo $link; ?>" class="title"><?php echo $title; ?></a>
</li>

If the next item does not have the same parent id and we have a sub-menu declared then close the sub-menu <ul>

            <?php if ( $menuitems[ $count + 1 ]->menu_item_parent != $parent_id && $submenu ): ?>
</ul>
<?php $submenu = false; endif; ?>

<?php endif; ?>

Again, if the next item in the array does not have the same parent id close the <li>

    <?php if ( $menuitems[ $count + 1 ]->menu_item_parent != $parent_id ): ?>
</li>
<?php $submenu = false; endif; ?>

<?php $count++; endforeach; ?>

</ul>
</nav>

How to display 3 levels of hierarchical menu in WordPress using wp_get_nav_menu_items?

After making changes in above code,this things work for me.

    $menu_name = 'primary-menu'; 
$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'));
$ParentArray = array();
foreach ($menuitems as $item) {
// if (!empty($item->menu_item_parent) && !in_array($item->menu_item_parent, $ParentArray)) {
array_push($ParentArray, $item->ID);
// }
}?>
<nav>
<ul class="main-nav">
<?php
$count = 0;
$submenu = false;
foreach ($menuitems as $item):
$link = $item->url;
$title = $item->title;
// item does not have a parent so menu_item_parent equals 0 (false)
if (!$item->menu_item_parent):
// save this id for later comparison with sub-menu items
$parent_id = $item->ID; ?>

<li class="item">
<a href="
<?php echo $link; ?>" class="title">
<?php echo $title; ?>
</a>
<?php endif; ?>
<?php if (in_array($item->menu_item_parent, $ParentArray)): ?>
<?php if (!$submenu): $submenu = true; ?>
<ul class="sub-menu">
<?php endif; ?>
<li class="item">
<a href="
<?php echo $link; ?>" class="title">
<?php echo $title; ?>
</a>
</li>
<?php if (!isset($item[$count + 1]) || $item[$count + 1]->menu_item_parent != $parent_id && $submenu): ?>
</ul>
<?php
$submenu = false;
endif;
?>
<?php endif; ?>
<?php if (!isset($menuitems[$count + 1]) || $menuitems[$count + 1]->menu_item_parent != $parent_id): ?>
</li>
<?php
$submenu = false;
?>
<?php
$count++;
endforeach;
?>
</ul>
</nav>

WordPress Menu: On click of parent menu item, only display sub navigation children of that link

Is there a way to alter the function I have to pull in only children
for a specific parent menu item?

For that purpose, yes, there is.

Try the following function (replace the existing cr_get_menu_items() function):

function cr_get_menu_items($menu_location, $parent = -1)
{
$locations = get_nav_menu_locations();
$menu = get_term($locations[$menu_location], 'nav_menu');
$items = wp_get_nav_menu_items($menu->term_id);

if ( is_numeric( $parent ) && $parent >= 0 ) {
$_id = (int) $parent;
foreach ( $items as $i => $item ) {
if ( $_id !== (int) $item->menu_item_parent ) {
unset( $items[ $i ] );
}
}
}

return $items;
}

Usage examples:

$nav = cr_get_menu_items( 'navigation_menu' );    // Get all menu items.
$nav = cr_get_menu_items( 'navigation_menu', 0 ); // Get menu items whose parent ID is 0

UPDATE

After I re-read your question, this is the function that you likely need:

// $items is the menu items array that you retrieved using `cr_get_menu_items()`,
// or other functions which return valid `nav_menu` items.
function cr_get_submenu_items( array $items, $parent ) {
$parent = (int) $parent;

$list = [];
foreach ( $items as $item ) {
if ( $parent === (int) $item->menu_item_parent ) {
$list[] = $item;
}
}

return $list;
}

UPDATE #2

Here's how you would/could use cr_get_menu_items() along with cr_get_submenu_items():

<?php $nav = cr_get_menu_items('navigation_menu') ?>

<!-- Display parent items. -->
<?php $nav = cr_get_menu_items('navigation_menu') ?>
<?php foreach ($nav as $link):
if ($link->menu_item_parent == 0) : ?>
<a class="main-nav" href="<?= $link->url ?>"><?= $link->title ?></a>
<?php endif; endforeach; ?>

<!-- Display children items. (in its own wrapper `div`/`ul`/etc.) -->
<?php $_ids = []; ?>
<?php foreach ($nav as $link):
$parent = (int) $link->menu_item_parent;
if ( 0 !== $parent && ! in_array( $parent, $_ids ) ) : ?>
<!-- This `div` is just an example wrapper. -->
<div class="menu-<?= $parent ?>-subnav">
<?php foreach ( cr_get_submenu_items( $nav, $parent ) as $clink ): ?>
<a href="<?= $clink->url ?>"><?= $clink->title ?></a>
<?php endforeach; ?>
<?php $_ids[] = $link->menu_item_parent; ?>
</div>
<?php endif; endforeach; ?>

How can I get wp_get_nav_menu_items to group child items with parents?

        // wordpress does not group child menu items with parent menu items
$menuLocations = get_nav_menu_locations();
$navbar_items = wp_get_nav_menu_items($menuLocations['Navbar']);
$child_items = [];

// pull all child menu items into separate object
foreach ($navbar_items as $key => $item) {
if ($item->menu_item_parent) {
array_push($child_items, $item);
unset($navbar_items[$key]);
}
}

// push child items into their parent item in the original object
foreach ($navbar_items as $item) {
foreach ($child_items as $key => $child) {
if ($child->menu_item_parent == $item->post_name) {
if (!$item->child_items) {
$item->child_items = [];
}

array_push($item->child_items, $child);
unset($child_items[$key]);
}
}
}

// return navbar object where child items are grouped with parents
return $navbar_items;

how to add custom wordpress Menu using just A tag output

add_theme_support('menu');function register_newtheme_menu(){    register_nav_menus(            array(                'main-menu'=>_('Main Menu')            )    );    }
add_action('init','register_newtheme_menu');

function add_menuclass($ulclass) { return preg_replace('/<a /', '<a class="navigation-link w-nav-link" ', $ulclass);}add_filter('wp_nav_menu','add_menuclass');?>


Related Topics



Leave a reply



Submit