0

I have an array in which I keep a menu of more than 100 elements and then print it as a CSS drop-down menu. There are 5 main menus and these have sub-menus, and some of these have sub-menus etc etc. Right now I'm hard coding the loops when printing them, but I'm sure there must be some clever way of doing it in only a few lines of code! This is what the beginning of the array looks like:

$menu = array(
    array(
        'title' => 'Travel tips',
        'url' => 'travel-tips',
        'sub' => array(
            array(
                'title' => 'Travel guide',
                'url' => 'travel-guide'),
            array(
                'title' => 'Places to visit',
                'url' => 'places-to-visit',
                'sub' => array(
                    array(
                        'title' => 'Ahu Akivi',
                        'url' => 'ahu-akivi'),
                    array(
                        'title' => 'Ahu Tongariki',
                        'url' => 'ahu-tongariki'),
                    array(
                        'title' => 'Anakena',
                        'url' => 'anakena'),
                    array(
                        'title' => 'Orongo',
                        'url' => 'orongo'),
                    array(
                        'title' => 'Rano Kau',
                        'url' => 'rano-kau'),
                    array(
                        'title' => 'Rano Raraku',
                        'url' => 'rano-raraku'),
                    array(
                        'title' => 'Vinapu',
                        'url' => 'vinapu'))),
            array(
                'title' => 'Things to do',
                'url' => 'things-to-do',
                'sub' => array(
                    array(
                        'title' => 'Beaches',
                        'url' => 'beaches'),
                    array(
                        'title' => 'Church',
                        'url' => 'church'),
                    array(
                        'title' => 'Fishing',
                        'url' => 'fishing'),

...and then it goes on and on. How can I loop this neatly and cleanly in only a few lines of code recursively without hard-coding the loops?

4 Answers 4

2

You'd want a self-calling function, if you don't want to specify the number of loops. Just add in the div/class/formatting you want in each loop.

function echoMenu($arr){
    foreach($arr as $subArr){
        if(!empty($subArr['sub'])){
            echo "<a href='{$subArr['url']}'>{$subArr['title']}</a>";
            echo "<div class='for-sub-links'>";
            echoMenu($subArr['sub']);
            echo "</div>";
        }else{
            echo "<div><a href='{$subArr['url']}'>{$subArr['title']}</a></div>";
        }   
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

You forgot to end line 5 with a semi-colon
A nice solution, although recursive is a more common name for a function that calls itself
Brain's a little frazzled at the end of the day... would you believe I couldn't think of the word recursive?
the word recursively is used in the question :)
Thank you for your answer, nice and clean! Though, it has 1 problem. For example, the URL of the button "Ahu Tongariki" should have the URL /travel-tips/places-to-visit/ahu-tongariki/, but with this solution it only has the URL /ahu-tongariki/. I fixed this by adding the current URL to an array $prevURL[] which I then use in the function call - echoMenu($subArr['sub'], $prevURL). I added an answer with this solution.
1
function recursive_menu($menu) {
  $output ='';
  foreach($menu as $m){
    $output .= '<li><a href="'.$m['url'].'">'.$m['title'].'</a>';
    if (isset($m['sub'])) {
      $output .= '<ul>';
      foreach( $m['sub'] as $item ) {
        $output.=recursive_menu($item);
      }
      $output .= '</ul>';
    } 
  }
  return $output.'</li>';  
}

then...

echo '<ul>'.recursive_menu($menu).'</ul>';

something like this...

Comments

1

Rather simple :

//mainly for demonstration / test purposes
function drawMenuItem($title, $url, $indent) {
    for ($i=0;$i<=$indent;$i++) echo "___";
    echo '<a href="'.$url.'">'.$title.'</a>'.'<br>';
}

//the actual "few lines of code recursively" function
function createMenuRecursively($menu, $indent) {
    foreach($menu as $menuItem){
        drawMenuItem($menuItem['title'], $menuItem['url'], $indent);
        if (is_array($menuItem['sub'])) {
            createMenuRecursively($menuItem['sub'], $indent+1);
        }
    }
}

createMenuRecursively($menu, 1);

Just using $indent for demonstration. Override drawMenuItem to do the CSS / ul / li stuff. The above outputs :

enter image description here override

Comments

0

Thanks to aynber I came up with a solution that also gives correct URLs:

function echoMenu($arr, $prevURL)
{
    if (is_array($prevURL))
    {
        foreach($prevURL as $url) //Collect all previous URLs into string
            $prevURLStr .= $url . '/';
    }

    foreach($arr as $key => $subArr)
    {
        echo '<li><a href="/' . $prevURLStr . $subArr['url'] . '">' . $subArr['title'] . '</a>';
        if(!empty($subArr['sub'])) //Has submenu - call function again to enter it
        {
            $prevURL[] = $subArr['url']; //Add this folder's URL to array

            echo '<ul>';
            echoMenu($subArr['sub'], $prevURL);
            echo '</ul>';

            $prevURL = array_pop($prevURL); //Remove last element
            $prevURL = @array_values($prevURL); //Remove empty remains
        } 
        echo '</li>';
    }
}

echo '<ul class="menu">';

echoMenu($menu, null);

echo '</ul>';

1 Comment

Good you got it to work, but "only a few lines of code recursively without hard-coding the loops" ..? :)

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.