4

I have a huge PHP array which holds the navigation structure of the site. It basically looks like this:

$navMap = array(
    array(
        'title' => 'Home',
    ), array(
        'title' => 'about us',
        'subNav' => array(
            array(
                'title' => 'Sub item 1',
                'subNav' => array(
                    array(
                        'title' => 'Sub sub item 1',
                    ),  array (
                        'title' => 'Sub sub item 2',
                    )
                ),
            ), array (
                'title' => 'Sub item 2',
            ), array (
                'title' => 'Sub item 3',
            )
        )
    )
);

I have a folder structure which would make the "Sub sub item 2" page link /pages/1/0/1.php since it is in the second of the primary nav items, the 1st of the secondary nav items and the 2nd of the tertiary nav items.

Instead of writing the links into the array which is prone to human error, I'm using PHP to add the links for me. This is what I'm doing at the moment:

for ($a = 0; $a < count($navMap); $a++) {
    $navMap[$a]['link'] = '/pages/'.$a.'.php';

    if (isset($navMap[$a]['subNav'])){

        for ($b = 0; $b < count($navMap[$a]['subNav']); $b++) {
            $navMap[$a]['subNav'][$b]['link'] = '/pages/'.$a.'/'.$b.'.php';

            if (isset($navMap[$a]['subNav'][$b]['subNav'])){

                for ($c = 0; $c < count($navMap[$a]['subNav'][$b]['subNav']); $c++) {
                    $navMap[$a]['subNav'][$b]['subNav'][$c]['link'] = '/pages/'.$a.'/'.$b.'/'.$c.'.php';
                }
            }

        }
    }
}

It's a really ugly solution though. Does anyone know a better way of adding the links to the array?

I'm after a solution that can detect an infinite number of levels on it's own with out having to add extra code for every extra level added. My current solution must be edited every time a new level is added to the $navMap.

Daniel Tonon
  • 9,261
  • 5
  • 61
  • 64

2 Answers2

1

You could use a foreach loop and string interpolation:

foreach ($navMap as $a => &$nm) {
    $nm['link'] = "/pages/$a.php";
    if (isset ($nm['subNav'])) {
        foreach ($nm['subNav'] as $b => &$sn) {
            $sn['link'] = "/pages/$a/$b.php";
            if (isset ($sn['subNav'])) {
                foreach ($sn['subNav'] as $c => &$sn2) {
                    $sn2['link'] = "/pages/$a/$b/$c.php";
                }
            }
        }
    }
}

The foreach iterates without having use the counters (but you can access them if needed as shown using the "$a =>" construct).

The &$nm accesses the actual variable instead of a copy so you can assign to it.

String interpolation allows you to construct more readable strings without having to use concatenation.

Rob
  • 341
  • 1
  • 3
  • 11
  • I'm hoping for something that can detect if there is a subNav item in the current array and if there is, it can call on a function that builds up and adds to the existing $navMap array. I'm expecting the answer will have array_push() in there somewhere. Your solution is a little bit neater but it has the same problem of not being able to detect a 4th or 5th level without having to alter the code. I'm after a solution that can detect an infinite number of levels on it's own with out having to add extra code for every extra level added. – Daniel Tonon Sep 06 '15 at 23:46
  • OK, I see. It sounded like you just wanted to tidy up the existing code. You can do this with iteration, but it's probably simpler to go with a recursive solution: I'll add another answer. – Rob Sep 07 '15 at 14:48
1

You probably want a recursive solution, not an iterative one.

function build_navMap(&$navMap,$link) {
    foreach ($navMap as $l => &$nm) {
        $nm['link'] = "$link/$l.php";
        if (isset ($nm['subNav'])) {
            build_navMap($nm['subNav'],"$link/$l");
        }
    }
}
build_navMap($navMap,"/pages");

This function calls itself for each level of the array. Recursion can be a little difficult to understand at first, but think of it as a new clean slate each function call with a little less of the array left (passed in through parameters) to traverse each time.

Rob
  • 341
  • 1
  • 3
  • 11