Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP: Loop through multidimensional array and establish parent-child relationships between array items

I am developing a content management system and I have run into an issue with child-parent relationships of items in the CMS.

Basically I have a system that can create pages and when a page is created you can select a parent page for sub-navigation. This is all fine and dandy until I try to generate the navigation from the DB.

I'm not sure if some sort of join would be better but I prefer to get all the data in an array and manipulate the array with php.

My array of results from the DB is like this:

Array
(
    [0] => Array
    (
        [id] => 27
        [name] => home
        [link] => home.html
        [parent] => 0
    )

    [1] => Array
    (
        [id] => 30
        [name] => about
        [link] => about.html
        [parent] => 27
    )
)

I need to loop through an array like this that can have any number of navigation and intelligently sort it into its parent child relationships. I was able to do it but only one level deep. It needs to manage children with children with children etc. with an infinite number of layers and output it to HTML unordered nested lists.

<ul>
  <li>
  <a>Nav</a>
     <ul>
        <li>
           <a>Subnav</a>
             <ul>
                 <li>
                    <a>Sub Sub nav</a>
                 </li>
             </ul>
        </li>
     </ul>
  <li>
<ul>

Etc. ...

like image 932
Laurence Avatar asked Dec 03 '22 00:12

Laurence


2 Answers

I don't think you should get into objects. Plus I think it would just be extra work to generate objects and etc. In my opinion you should loop through the array and generate a multidimensional array that represents the navigational hierarchy and then loop the generated array recursively to generate your HTML. I've done a sample code for you, it works the way you want it to but you probably want to make some changes.

functions

// Generate your multidimensional array from the linear array
function GenerateNavArray($arr, $parent = 0)
{
    $pages = Array();
    foreach($arr as $page)
    {
        if($page['parent'] == $parent)
        {
            $page['sub'] = isset($page['sub']) ? $page['sub'] : GenerateNavArray($arr, $page['id']);
            $pages[] = $page;
        }
    }
    return $pages;
}

// loop the multidimensional array recursively to generate the HTML
function GenerateNavHTML($nav)
{
    $html = '';
    foreach($nav as $page)
    {
        $html .= '<ul><li>';
        $html .= '<a href="' . $page['link'] . '">' . $page['name'] . '</a>';
        $html .= GenerateNavHTML($page['sub']);
        $html .= '</li></ul>';
    }
    return $html;
}

** sample usage **

$nav = Array
(
    Array
    (
        'id' => 27,
        'name' => 'home',
        'link' => 'home.html',
        'parent' => 0
    ),
    Array
    (
        'id' => 30,
        'name' => 'about',
        'link' => 'about.html',
        'parent' => 27
    )
);

$navarray = GenerateNavArray($nav);
echo GenerateNavHTML($navarray);

You can probably do both things in one step but I think it's neater to generate the multidimensional array first. Goodluck!

like image 179
Saad Imran. Avatar answered Jan 04 '23 05:01

Saad Imran.


Saad Imran's solution works alright except for if you would like multiple navigation items in the same list. I had to change a few lines to get it to generate validated list of items. I also added indents to make generated code more readable. I'm using spaces but this can be easily changed to tabs if you prefer, just replace 4 spaces with "\t". (note you must use double quotes, not single quotes, otherwise php doesn't replace with a tab character but actually a \t)

I am using this in codeigniter 2.1.0 with superfish (PHP5)

function GenerateNavHTML($nav, $tabs = "")
{
    $tab="    ";
    $html = "\n$tabs<ul class=\"sf-menu\">\n";
    foreach($nav as $page)
    {
        //check if page is currently being viewed
        if($page['link'] == uri_string()) {
            $html .= "$tabs$tab<li class=\"current\">";
        } else {
            $html .= "$tabs$tab<li>";
        }
        $html .= "<a href=\"$page[link]\">$page[name]</a>";
        //Don't generate empty lists
        if(isset($page['sub'][0])) {
            $html .= $this->GenerateNavHTML($page['sub'], $tabs.$tab);
        }
        $html .= "</li>\n";
    }
    $html .= $tabs."</ul>\n";

    return $html;
}

I was getting (switched to ol for clarification)

1. Home  
1. About Us  
1. Products  
    1. sub-product 1  
    1. sub-product 2  
1. Contact  

now i get

1. Home  
2. About Us  
3. Products  
    1. sub-product 1  
    2. sub-product 2  
4. Contact  

Nicely generated HTML

<ul class="sf-menu">
    <li class="current"><a href="index.html">Home</a></li>
    <li><a href="about.html">About Us</a></li>
    <li><a href="products.html">Products</a>
        <ul class="sf-menu">
            <li><a href="products-sub1.html">sub-product 1</a></li>
            <li><a href="products-sub2.html">sub-product 2</a></li>
        </ul>
    </li>
    <li><a href="contact.html">Contact</a></li>
</ul>
like image 42
vortextangent Avatar answered Jan 04 '23 06:01

vortextangent