1

I have a flat array full of navigation menu items in WP. Each item knows its parent, but none of them seem to know if they have children.

            Array
(
    [0] => stdClass Object
        (
            [ID] => 22
            [menu_item_parent] => 0
        )
    [1] => stdClass Object
        (
            [ID] => 108
            [menu_item_parent] => 22
        )
    [2] => stdClass Object
        (
            [ID] => 117
            [menu_item_parent] => 108
        )
    [3] => stdClass Object
        (
            [ID] => 118
            [menu_item_parent] => 108
        )
    [4] => stdClass Object
        (
            [ID] => 106
            [menu_item_parent] => 22
        )
    [5] => stdClass Object
        (
            [ID] => 119
            [menu_item_parent] => 106
        )
    [6] => stdClass Object
        (
            [ID] => 120
            [menu_item_parent] => 106
        )
    [7] => stdClass Object
        (
            [ID] => 23
            [menu_item_parent] => 0
        )
)

I've tried approaching it with conditional logic a few different ways (and each time hard-coding values which isn't ideal), but I keep coding myself into a mess. How can I iterate over the array to produce a result like this?

<ul>
    <li>22
        <ul class="child">
            <li>108
                <ul class="grandchild">
                    <li>117</li>
                    <li>118</li>
                </ul>
            </li>
            <li>106
                <ul class="grandchild">
                    <li>119</li>
                    <li>120</li>
                </ul>
            </li>           
        </ul>
    </li>
    <li>23</li>
</ul>
5
  • Will your source array be always sorted from root to nodes? Commented Nov 27, 2012 at 10:55
  • Yeah, that's the way WP spits them out. Commented Nov 27, 2012 at 11:38
  • possible duplicate of How to Flatten a Multidimensional Array? Commented Nov 27, 2012 at 17:35
  • Well no, not really @Maerlyn. That's the exact opposite of what I was trying to achieve. Commented Nov 28, 2012 at 10:29
  • I think this is a legitimate question. I don't know why it received a close vote. Commented Nov 29, 2012 at 9:47

3 Answers 3

1

You need to identify children for the nodes, so that you can put them in the structure you need. To do so, you will need to iterate over the elements and reorganise them. Below is a rough code of what to do; not tested it.

$new = array();

foreach ($array as $elm)
{
    if ($elm->menu_item_parent != 0)
    {
        foreach($new as $it)
        {
            $found = false;
            if ($it->ID == $elm->menu_item_parent)
            {
                $it->children[] = $elm;
                $found = true; // Found the parent break loop
            } else if (isset($it->children))
            {
                foreach ($it->children as $child)
                {
                    if ($child->ID == $elm->menu_item_parent)
                    {
                        $child->children[] == $elm;
                        $found = true;
                        break;
                    }
                }

                if ($found) break;
            }
        }
    } else {
        $new[] = $elm; // Parent element - add directly
    }
}

The array $new should now contain elements with child nodes that you can iterate over to produce the required result.

This works by looping over the current array, identifying parents, and their child nodes and organising them as such. Will work only two levels deep; if you need to go deeper, the code may need reworking to organise it better.

Sign up to request clarification or add additional context in comments.

1 Comment

I went with my own solution, but an upvote for the input. Cheers.
1

I would like to start with making separate parent(main) array and child array (or we can make it a long multi-dimensional array)

foreach($arr AS $value)
{
    if($value->menu_item_parent == 0)
        $parentArr[] = $value->ID;
    else
        $childArr[$value->menu_item_parent][] = $value->ID; 
}

A recursive function will help to obtain a little Global solution to make child and grandchild. Consider this,

$str = '';

echo "<ul>";
foreach($parentArr AS $pValue)
{
    echo '<li>'.$pValue;
        if(in_array($pValue, array_keys($childArr)))
        {
            echo '<ul class="child">';
            $result= childs($pValue); //calling a recursive function (In case one have larger child tree)
            echo $result;
            echo '</ul>';
        }
    echo '</li>';
}
echo '</ul>';

function childs($val)
{
    global $childArr;
    global $str;

    foreach($childArr[$val] AS $cValue)
    {
        $str.= '<li>'.$cValue;

        if(in_array($cValue, array_keys($childArr)))
        {
            $str.= '<ul class="grand_child">';
            childs($cValue);
            $str.= '</ul>';
        }
        $str.= '</li>';
    }

    return $str;
}

There must be a better approach then this to handle this kind of problem.

1 Comment

I went with my own solution, but an upvote for the input. Cheers.
0

I ended up doing it like this:

<?php 
$items = wp_get_nav_menu_items('primary'); // original flat array
foreach ($items as $item) {
    $pages[$item->menu_item_parent][] = $item; // create new array
}
foreach ($pages[0] as $parent) {
    if (isset($pages[$parent->ID])) { // if page has children
        echo '<li>'.$parent->ID;
        echo '<ul class="children">';
        foreach ($pages[$parent->ID] as $child) {
            if (isset($pages[$child->ID])) { // child has children
                echo '<li>'.$child->ID;
                echo '<ul class="grandchildren">';
                foreach ($pages[$child->ID] as $grandchild) {
                    echo '<li>'.$grandchild->ID.'</li>';
                }
                echo '</ul></li>';
            } else {
                echo '<li>'.$child->ID.'</li>'; // child has no children
            }
        }
        echo '</ul></li>';
    } else { // page has no children
        echo '<li>'.$parent->ID.'</li>';
    }
}
?>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.