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

旧城冷巷雨未停 提交于 2019-11-29 07:47:50

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!

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>
Qqwy

The easiest way of doing this would probably be using objects. These can be easily manipulated and also created quite easily from the array you get from the database.

An example, every object has:

  1. an ID
  2. a name
  3. a link
  4. an object reference to the parent object
  5. a list of child objects

You will need a static function that is able to find out what object has a certain database ID. This is done during the creation of a new object, by putting a reference in a static list. This list can then be called and checked against at runtime.

The rest is quite straightforward:

$arr = get_array_from_database();
foreach($arr as $node){
    $parent_object = get_parent_object($node_id);
    $parent_object.subnodes[] = new NodeObject($node);
}

As for returning the objects in a list, this is best done recursively;

function return_in_list($objects)
{
    foreach($objects as $node)
    {
        echo '<li>';
        echo '<a>' + node.link + '</a>';

        if(node.subnodes.length > 0)
        {
            echo '<ul>';
            return_in_list($node.subnodes);
            echo '</ul>';
        }
    puts '</li>'
    }
}

I am working on the same project, and I have not found found the need to use objects. The database pretty much takes care of the structure, and php functions can do the rest. My solution was have a parent field for pages that points to a section name, as you have, but also to add a parent field for the sections table, so that sections can point to other sections as parents. It is still very manageable because there are only 2 parent fields to keep track of, one for each table, and I can nest my structure as many levels as necessary in the future.

So for dynamic tree creation, I am going to recursively check for parents until I hit a null, which indicates to me that the current element sits on the doc root. This way we don't need to know any details of the current page structure in the function code, and we can focus on just adding and arranging pages in mysql. Since another poster showed some menu html, I thought I would add an example here for a dynamic breadcrumb path because the themes are so similar.

DATA

// Sample 3 x 2 page array (php / html pages)
// D1 key = page name
// D2 element 1 = page name for menu display
// D2 element 2 = parent name (section)
$pages = Array
(
  'sample-page-1.php' => Array ( 'M' => 'page 1', 'P' => null ),
  'sample-page-2.php' => Array ( 'M' => 'page 2', 'P' => 'hello' ),
  'sample-page-3.php' => Array ( 'M' => 'page 3', 'P' => 'world' )
);

// Sample 2 x 1 section array (parent directories)
// D1 key = section name
// D2 element = parent name (if null, assume root)
$sections = Array
( 
  'hello' => null,
  'world' => 'hello'
);

$sep = ' > ';       // Path seperator
$site = 'test.com'; // Home string

FUNCTIONS

// Echo paragraph to browser
function html_pp ( $text )
{
  echo PHP_EOL . '<p>' . sprintf ( $text ) . '</p>' . PHP_EOL;
}

// Get breadcrumb for given page
function breadcrumb ( $page )
{
  // Reference variables in parent scope
  global $pages;
  global $sections;
  global $sep;
  global $site;

  // Get page data from array
  $menu   = $pages [ $page ] [ 'M' ];
  $parent = $pages [ $page ] [ 'P' ];

  if  ( $parent == null )
  {
    $path = $site . $sep . $menu;
  }
  else
  {
    $path = $site . $sep . get_path ( $parent ) . $sep . $menu;
  }
  return $path;
}

// Trace ancestry back to root
function get_path ( $parent )
{
  // Reference variables in parent scope
  global $sections;
  global $sep;

  if ( $sections [ $parent ] == null )
  {
    // No more parents
    return $parent;
  }
  else
  {
    // Get next parent through recursive call
    return get_path ( $sections [ $parent ] ) . $sep . $parent;
  }
}

USAGE

// Get breadcrumbs by page name
$p1 = 'sample-page-1.php';
$p2 = 'sample-page-2.php';
$p3 = 'sample-page-3.php';

html_pp ( $p1 . ' || ' . breadcrumb ( $p1 ) );
html_pp ( $p2 . ' || ' . breadcrumb ( $p2 ) );
html_pp ( $p3 . ' || ' . breadcrumb ( $p3 ) );

// or use foreach to list all pages
foreach ( $pages as $page => $data)
{
  html_pp ( $page . ' || ' . breadcrumb ( $page ) );
}

OUTPUT

sample-page-1.php || test.com > page 1

sample-page-2.php || test.com > hello > page 2

sample-page-3.php || test.com > hello > world > page 3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!