2

I have a 3 level navigation:

Home
   > submenu1
       >> sub3       
   > submenu
       >> sub4       
       >> sub5       // current page
About
   > about2
       >> sub6       
   > about3
       >> sub7       

I am trying to get each navigation level separately,

With the example nav above, being on the sub5 page, I would need

Current 1st level nav: Home

Current parallel 2nd level nav: submenu1, submenu (who are both under Home)

Current parallel 3rd level nav: sub4, sub5 (who are menu under submenu)

I will need to modify the elements and styles of the menu and will need to work with menu items in php, such as:

$menu_name = 'topnav';
if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) {
$menu = wp_get_nav_menu_object( $locations[ $menu_name ] );
$menu_items = wp_get_nav_menu_items($menu->term_id);

foreach ( (array) $menu_items as $key => $menu_item ) {
    $title = $menu_item->title;
    $url = $menu_item->url;
    [...]

Please help getting the navigation layers separately to show up as explained above.

I got the 1st and 3rd level working, but can't get the current parallel 2nd level nav to show up correctly.

Thanks.

Current Code: 2nd nav works:

<?php
$menu_name = 'topnav';
if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) {

    $menu = wp_get_nav_menu_object( $locations[ $menu_name ] );

    $menu_items = wp_get_nav_menu_items( $menu->term_id );

    // Convert Objects to Arrays, Enables us to use Array Filter
    $json  = json_encode($menu_items);
    $menu_items = json_decode($json, true);

    // Current Page
    $child = get_the_id();


    $current_level = array_filter( $menu_items, function($v, $k) use ($child) {
    return $v['object_id'] == $child;
    }, ARRAY_FILTER_USE_BOTH );

    $current_level_keys = array_keys($current_level);
    $parent = $current_level[$current_level_keys[0]]['menu_item_parent'];

    if( !empty( $parent ) )
    {
    $current_level_items = array_filter( $menu_items, function($v, $k) use ($parent)  {
        return $v['menu_item_parent'] == $parent;
    }, ARRAY_FILTER_USE_BOTH );
    } else {
    $current_level_items = $current_level[$current_level_keys[0]];
    }

    //echo '1:';
    //echo '<pre>';
    //print_r($current_level_items);
    //echo '</pre>';

    //foreach ($current_level_items as $k => $v) {
    //  echo '<li><a href="#">'.$v['title'].'</a></li>';
    //}

    $parent_level = array_filter( $menu_items, function($v, $k) use ($parent) {
    return $v['ID'] == $parent;
    }, ARRAY_FILTER_USE_BOTH );

    $parent_level_keys = array_keys($parent_level);
    $grand_parent = $parent_level[$parent_level_keys[0]]['menu_item_parent'];

    if( !empty( $grand_parent ) )
    {
    $parent_level_items = array_filter( $menu_items, function($v, $k) use ($grand_parent)  {
        return $v['menu_item_parent'] == $grand_parent;
    }, ARRAY_FILTER_USE_BOTH );
    } else {
    $parent_level_items = $parent_level[$parent_level_keys[0]];
    }

    //echo '2:';
    //echo '<pre>';
    //print_r($parent_level_items);
    //echo '</pre>';
    //foreach ($parent_level_items as $k => $v) {
    //echo '<li><a href="#">'.$v['title'].'</a></li>';
    //}

    $grand_parent_level = array_filter( $menu_items, function($v, $k) use ($grand_parent) {
    return $v['ID'] == $grand_parent;
    }, ARRAY_FILTER_USE_BOTH );

    $grand_parent_level_keys = array_keys($grand_parent_level);
    $great_grand_parent = $grand_parent_level[$grand_parent_level_keys[0]];

    if( !empty( $parent ) ) {
        if( !empty( $great_grand_parent ) ) {
        echo '<li class="custom-page-title">'.$great_grand_parent['title'].'</li>';
        if (is_array($parent_level_items)) {
            foreach ($parent_level_items as $k => $v) {
                echo '<li><a href="'.$v['url'].'">'.$v['title'].'</a></li>';
            }
        }
        } else {
        echo '<li class="custom-page-title">'.$parent_level_items['title'].'</li>';
        if (is_array($current_level_items)) {
            foreach ($current_level_items as $k => $v) {
                echo '<li><a href="'.$v['url'].'">'.$v['title'].'</a></li>';
            }
        }
        }
    }
    //echo '3:';
    //echo '<pre>';
    //print_r($great_grand_parent);
    //echo '</pre>';

}
?>

However if there are duplicate (3rd level) pages in the menu, this script only takes the first parent... Could it be the last or the real parent (from url path maybe?)

Duplicate page in menu issue:

Home
   > submenu1
       >> sub3
   > submenu
       >> sub4       
       >> sub5       // current page
About
   > about2
       >> sub6       
   > about3
       >> sub7
       >> sub5       // duplicate page

When visiting the duplicate page (sub5), the parent returned is the first one (Home > submenu), when it should be (About > about3)

Please help getting this fixed...

Luc Laverdure
  • 1,398
  • 2
  • 19
  • 36
  • Can you please share your code you used for getting 1st and 3rd level ? – Shahbaz A. Aug 19 '19 at 08:06
  • Can you please explain visually the case you are talking about ? Just like you did at the start of your post. – Shahbaz A. Aug 19 '19 at 16:52
  • Just added a visual as per requested. – Luc Laverdure Aug 19 '19 at 16:58
  • It won't work like this. You should not have duplicates, if you really need the duplicates then you will have to pick one parent chain either the first or the last. – Shahbaz A. Aug 19 '19 at 17:05
  • The best way to handle this is to not have duplicates at all. Because duplicates can't be allowed for what you are trying to do. Think for a second, both chains are valid for one item. How to pick one, and there should not be multiple paths styled. System does not know which path you are traversing, unless we look into referrers which becomes a long story. – Shahbaz A. Aug 19 '19 at 17:09
  • ok, can I select the last parent matching path then, rather than the first path matched? – Luc Laverdure Aug 19 '19 at 17:12
  • Added the ability to select either first or last parent chain. – Shahbaz A. Aug 20 '19 at 07:36

1 Answers1

2

Edit:

To address the duplicate situation you have to add a line of code. You can add one of the below lines based on the parent chain you want to select. I am also adding these lines in the actual code to show you where they go.

// Get First Parent Chain
$current_level = array_values(array_slice($current_level, 0, 1));

// Get Last Parent Chain
$current_level = array_values(array_slice($current_level, -1, 1));

Original Answer: ( Also updated with the above lines )

This code will give you all levels items, it is written for only three levels as per your requirements but you can use the logic and reiterate the code to as many levels as needed, or better yet write up something recursive.

$menu_name = 'topnav';
if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) {

    $menu = wp_get_nav_menu_object( $locations[ $menu_name ] );

    $menu_items = wp_get_nav_menu_items( $menu->term_id );

    // Convert Objects to Arrays, Enables us to use Array Filter
    $json  = json_encode($menu_items);
    $menu_items = json_decode($json, true);

    // Current Page
    $child = get_the_id();


    $current_level = array_filter( $menu_items, function($v, $k) use ($child) {
        return $v['object_id'] == $child;
    }, ARRAY_FILTER_USE_BOTH );

    // Get First Parent Chain ( Uncomment below line if you want to use this )
    //$current_level = array_values(array_slice($current_level, 0, 1));

    // Get Last Parent Chain ( Uncomment below line if you want to use this )
    //$current_level = array_values(array_slice($current_level, -1, 1));


    $current_level_keys = array_keys($current_level);
    $parent = $current_level[$current_level_keys[0]]['menu_item_parent'];

    if( !empty( $parent ) )
    {
        $current_level_items = array_filter( $menu_items, function($v, $k) use ($parent)  {
            return $v['menu_item_parent'] == $parent;
        }, ARRAY_FILTER_USE_BOTH );
    } else {
        $current_level_items = $current_level[$current_level_keys[0]];
    }

    echo '<pre>';
    print_r($current_level_items);
    echo '</pre>';

    $parent_level = array_filter( $menu_items, function($v, $k) use ($parent) {
        return $v['ID'] == $parent;
    }, ARRAY_FILTER_USE_BOTH );

    $parent_level_keys = array_keys($parent_level);
    $grand_parent = $parent_level[$parent_level_keys[0]]['menu_item_parent'];

    if( !empty( $grand_parent ) )
    {
        $parent_level_items = array_filter( $menu_items, function($v, $k) use ($grand_parent)  {
            return $v['menu_item_parent'] == $grand_parent;
        }, ARRAY_FILTER_USE_BOTH );
    } else {
        $parent_level_items = $parent_level[$parent_level_keys[0]];
    }

    echo '<pre>';
    print_r($parent_level_items);
    echo '</pre>';

    $grand_parent_level = array_filter( $menu_items, function($v, $k) use ($grand_parent) {
        return $v['ID'] == $grand_parent;
    }, ARRAY_FILTER_USE_BOTH );

    $grand_parent_level_keys = array_keys($grand_parent_level);
    $great_grand_parent = $grand_parent_level[$grand_parent_level_keys[0]];

    echo '<pre>';
    print_r($great_grand_parent);
    echo '</pre>';

}

I have tried to write it as self explanatory, but if you have any questions i will be happy to help.

Shahbaz A.
  • 4,047
  • 4
  • 34
  • 55
  • The code works if I am at the third level in nav, but not if current page is at second level... can you please alter your code so the menu items work at all levels? – Luc Laverdure Aug 19 '19 at 13:51
  • for example if I am under "submenu" (at 2nd level), 1st level nav should be "home", 2nd level nav should be "submenu1, submenu", and third level nav should be "sub4, sub5" – Luc Laverdure Aug 19 '19 at 14:02
  • Also if there are two menu items that link to the same page, the parents are incorrect... – Luc Laverdure Aug 19 '19 at 15:37
  • @LucLaverdure The code should work for all levels. However that duplicate thing is not something that i tested. Could you explain a bit more, like with example use case in your question ? – Shahbaz A. Aug 19 '19 at 16:48