Hey,

How do I implement multi-level dynamic navigation with php? Assuming my database would be like:

id    name    parent_id
----------------------------
1    page     0
2    sub1     1
3    sub2     1
4    sub12    2

I've tried many ways, but haven't found any 'good' methods/implementations so far.

Now I have:

$refs = array();
	  $list = array();
	  $pages = array();
	  
	  foreach ($query->result_array() as $row)
	  {
	     $pages[] = $row;
	  }
	  
	  foreach ($pages as $data)
	  {
		$thisref = &$refs[ $data['page_id'] ];
		$thisref['parent'] = $data['parent'];
		$thisref['urlkey'] = $data['urlkey'];
		$thisref['name'] = $data['name'];
		$thisref['page_id'] = $data['page_id'];
		
		if ($data['parent'] == 0) {
		    $list[ $data['page_id'] ] = &$thisref;
		} else {
		    $refs[ $data['parent'] ]['children'][ $data['page_id'] ] = &$thisref;
		}
	  }

print_r($list);

Thanks.

i just created a function to make dynamic multi-level menus.

it uses an array to make the menus. i think if you can get the data from the database into the right format for the function it should work nicely for you.
example:
$menu = array( 'href'=>'path/to/page','title'=>'Page Title' );
$menu = array( 'href'=>'path/to/page','title'=>'Page title' );

i will have to modify the code to work for you since its built into my framework. let me know if you want to try it out.

i just created a function to make dynamic multi-level menus.

it uses an array to make the menus. i think if you can get the data from the database into the right format for the function it should work nicely for you.
example:
$menu = array( 'href'=>'path/to/page','title'=>'Page Title' );
$menu = array( 'href'=>'path/to/page','title'=>'Page title' );

i will have to modify the code to work for you since its built into my framework. let me know if you want to try it out.

Sure, I could look at it. I'm using CodeIgniter framework myself.

I hate javascript so I just used a prebuilt drop down menu system and created a function to make the lists that it uses.

you can get the js and css from http://www.leigeber.com/2008/11/drop-down-menu/

here is the php function:

function buildMenu( $array=null,$i=0 ) {
        $i == 0 ? $menuClass = ' class="menu" id="menu"' : $menuClass = '';
        $list = str_repeat( "\t",$i ) . "<ul{$menuClass}>\n";
        $i2 = 0;
        foreach( (array)$array as $name => $data ) {
            $name = ucwords($name);
            $subm = '';
            $nList = '';
            if ( isset( $data['submenu'] ) ) {
                $subm = $i !== 0 ? ' class="sub"' : '';
                $nList = "\n" . buildMenu( $data['submenu'],( $i + 1 ) ) . "\n" . str_repeat( "\t",( $i + 1 ) );
            }
            $topl = $i > 0 && $i2 == 0 ? ' class="topline"' : '';
            $menu = $i == 0 ? ' class="menulink"' : '';
            $list .= str_repeat( "\t",( $i + 1 ) ) . "<li{$topl}><a{$subm}{$menu} href=\"{$data['href']}\" title=\"{$data['title']}\">{$name}</a>{$nList}</li>\n";
            $i2++;
        }
        $list .= str_repeat( "\t",$i ) . "</ul>";
        return $list;
    }

to use just run buildMenu( $menu ); where $menu is the array i mentioned in an earlier post.

I hate javascript so I just used a prebuilt drop down menu system and created a function to make the lists that it uses.

you can get the js and css from http://www.leigeber.com/2008/11/drop-down-menu/

here is the php function:

function buildMenu( $array=null,$i=0 ) {
        $i == 0 ? $menuClass = ' class="menu" id="menu"' : $menuClass = '';
        $list = str_repeat( "\t",$i ) . "<ul{$menuClass}>\n";
        $i2 = 0;
        foreach( (array)$array as $name => $data ) {
            $name = ucwords($name);
            $subm = '';
            $nList = '';
            if ( isset( $data['submenu'] ) ) {
                $subm = $i !== 0 ? ' class="sub"' : '';
                $nList = "\n" . buildMenu( $data['submenu'],( $i + 1 ) ) . "\n" . str_repeat( "\t",( $i + 1 ) );
            }
            $topl = $i > 0 && $i2 == 0 ? ' class="topline"' : '';
            $menu = $i == 0 ? ' class="menulink"' : '';
            $list .= str_repeat( "\t",( $i + 1 ) ) . "<li{$topl}><a{$subm}{$menu} href=\"{$data['href']}\" title=\"{$data['title']}\">{$name}</a>{$nList}</li>\n";
            $i2++;
        }
        $list .= str_repeat( "\t",$i ) . "</ul>";
        return $list;
    }

to use just run buildMenu( $menu ); where $menu is the array i mentioned in an earlier post.

Thanks for the reply,
But what about the function that builds the menu array? That I kinda meant.

When I get pages from database, I get:

$pages = array (
	array ("page_id" => 1, "name" => "Page 1", "parent_id" => 0),
	array ("page_id" => 2, "name" => "Page 2", "parent_id" => 0),
	array ("page_id" => 3, "name" => "Sub 1", "parent_id" => 1),
	array ("page_id" => 4, "name" => "Sub 2", "parent_id" => 2),
	array ("page_id" => 5, "name" => "Sub of sub", "parent_id" => 3),
	) ;

How do I get it into this (into php array):

$pages = array (
		array ("page_id" => 1, "name" => "Page 1", "parent_id" => 0,
		"children" => array (array ("page_id" => 3, "name" => "Sub 1", "parent_id" => 1))),

		array ("page_id" => 2, "name" => "Page 2", "parent_id" => 0,
		"children" => array ("page_id" => 4, "name" => "Sub 2", "parent_id" => 2,
			"children" => array ("page_id" => 5, "name" => "Sub of sub", "parent_id" => 4))),
	);

I must of misread. I thought you were looking for a good dynamic multi-level nav system. I was just giving you that. I figured if you liked it you could change the db to fit its needs or change the function. Sorry for wasting your time.

your parent ids don't match up. it would be hard to convert that. if you fix that problem, i could easily type some code to convert that.

your parent ids don't match up. it would be hard to convert that. if you fix that problem, i could easily type some code to convert that.

Hey,
Yea they probably wont match, but you get the idea, that was more or less pseudo. I just made up that array. :)

No, that is why I want to populate the navigation / breadcrumb array from "pages" table. As mentioned in the first post, I just left some fields off cause they were irrelevant to the solution.

I got some working code, but it needs to be simplified and it has too many foreach loops. I had to do them since there was no key system to reference different parts easily. It has a few flaws I am trying to work out, but it does what you asked it to do. It won't go beyond "sub of sub" status.

Just upload this and try it. See if it what you were looking for. Maybe it will give you ideas. ????

<?php

function getChildren( $pid ) {
    global $pages;
    $child = array();
    foreach( $pages as $data ) {
        if ( $data['parent_id'] == $pid ) {
            $child[] = $data;
        }
    }
    if ( count( $child ) == 0 ) {
        return false;
    }
    return $child;
}
function getData( $pid ) {
    global $pages;
    foreach( $pages as $data ) {
        if ( $data['page_id'] == $pid ) {
            return $data;
        }
    }
    return false;
}

$pages = array (
	array ("page_id" => 1, "name" => "Page 1", "parent_id" => 0),
	array ("page_id" => 2, "name" => "Page 2", "parent_id" => 0),
	array ("page_id" => 3, "name" => "Sub 1", "parent_id" => 1),
	array ("page_id" => 4, "name" => "Sub 2", "parent_id" => 2),
	array ("page_id" => 5, "name" => "Sub of sub", "parent_id" => 3)
);

foreach( $pages as $key => $data ) {
    $child = array();
    if ( $child = getChildren( $data['page_id'] ) ) {
        $pages[$key]['children'] = $child;
    }
}

foreach( $pages as $key => $data ) {
    if ( isset( $data['children'] ) && $data['parent_id'] == 0 ) {
        foreach( $data['children'] as $key2 => $data2 ) {
            $pages[$key]['children'][$key2] = getData( $data2['page_id'] );
        }
    }
}

foreach( $pages as $key => $data ) {
    if ( $data['parent_id'] !== 0 ) {
        unset( $pages[$key] );
    }
}

echo '<pre>' . print_r( $pages,true ) . '</pre>';
exit;

?>

I am trying to think of a recursion method to do this, but its hurting my head to think about it since its that complicated.

This article has been dead for over six months. Start a new discussion instead.