0

Here's the situation: I have a menu that needs to be created dynamically from the database. The menu hierarchy is determined by a 'parent' column in the table (each entry has one parent or NULL if it is only a parent)

The problem is that I can't think of how I would dynamically do this, considering I need proper <ul><li><ul><li> structure for my drop-down menu. This requires that I have my 'foreach' of child pages, within the foreach of parent pages? If that makes sense, is there a solution?

FYI: The array I am working with returns:

array(31) { 
[0]=>  array(5)
     { ["id"]=>  string(2) "31" ["title"]=>  string(4) "Home" ["linkable"]=>  string(1) "1" ["parent"]=>  NULL ["override"]=>  string(1) " " } 
[1]=>  array(5)
     { ["id"]=>  string(2) "30" ["title"]=>  string(11) "Shop Online" ["linkable"]=>  string(1) "1" ["parent"]=> string(2) "31" ["override"]=>  string(4) "shop" } 

and on and on.

2 Answers 2

2

You need to write a recursive function to do this and have it call itself. I haven't tested this out, but I think it should get you started. I wouldn't endorse this function's efficiency since it runs through every item in the array and does a comparison even though you're going to only need an item or two from each run (likely).

PHP:

$arr = array(...);
function output_lis($parentID = NULL){
    global $arr;
    $stack = array(); //create a stack for our <li>'s 
    foreach($arr as $a){ 
        $str = '';
            //if the item's parent matches the parentID we're outputting...
        if($a['parent']===$parentID){ 
            $str.='<li>'.$a['title'];

                    //Pass this item's ID to the function as a parent, 
                    //and it will output the children
            $subStr = output_lis($a['id']);
            if($subStr){
                $str.='<ul>'.$subStr.'</ul>';
            }

            $str.='</li>';
            $stack[] = $str;
        }
    }
    //If we have <li>'s return a string 
    if(count($stack)>0){
        return join("\n",$stack);
    }

    //If no <li>'s in the stack, return false 
    return false;
}

Then output this on your page. Something like:

<ul>
    <?php echo output_lis(); ?>
</ul>

Here is my sample array:

$arr = array(
        array('title'=>'home','parent'=>NULL,'id'=>1), 
        array('title'=>'sub1','parent'=>1,'id'=>2), 
        array('title'=>'sub2','parent'=>1,'id'=>3), 
        array('title'=>'about us','parent'=>NULL,'id'=>4), 
        array('title'=>'sub3','parent'=>4,'id'=>5), 
        array('title'=>'sub4','parent'=>4,'id'=>6), 
    );
Sign up to request clarification or add additional context in comments.

4 Comments

After plugging it all in, I'm just getting the parent pages listed with no child pages. Looking at it more now though.
I forgot to wrap a <ul> around the output_lis() call within the function. See edited solution...
Still nothing but parents being output. The source shows only the <ul> wrapping the menu and the <li>'s created within, but only the <li>s for the elements with a parent of NULL.
On the plus side, I believe you. On the other side of that, I figured out how to do it using a couple functions I already had kicking around. Did it almost the same way though, using a recursive function.
0

No recursion necessary.

$parents = array();
$noparents = array();
foreach ($results as $ar) {
  if ( $ar['parent'] != NULL ) {
    $parents[$ar['parent']] = array();
    array_push($parents[$ar['parent']], $ar['title']);
  } else {
    array_push($noparents, $ar['title']);
  }
}

Now you have all of your links that belong to a parent listed in an array named after the parent. Your other links are in a separate array. You can join them if you like.

Extend it as is required for your application.

3 Comments

Would this be flexible enough to handle a three-tier menu? I have Page 1 -> Page 2 -> Page 3 in some situations.
One flaw here: There is no array defined for $parents[$ar['parent']] the first time, so array_push won't work.
I'm certain you could make it flexible enough. It's just a simple example.

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.