PHP:循环遍历多维数组并在数组项之间建立父子关系


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

我正在开发一个内容管理系统,我遇到了CMS中项目的父子关系问题。

基本上我有一个系统,可以创建页面,当一个页面被创建,你可以选择父页面的子导航。这一切都很好,直到我尝试从DB生成导航。

我不确定某种连接是否会更好,但我更喜欢在数组中获取所有数据并使用php操作数组。

我的数组结果从DB是这样的:

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

我需要循环遍历这样一个数组,它可以有任意数量的导航,并将其智能地排序到它的父子关系中。我能做到,但只有一层深。它需要管理无限层的子层的子层等,并将其输出到HTML无序嵌套列表。

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

等等…

我认为你不应该研究对象。另外,我认为生成对象等只是额外的工作。在我看来,您应该循环遍历数组并生成一个表示导航层次结构的多维数组,然后递归地循环生成的数组以生成HTML。我已经为你做了一个示例代码,它按你想要的方式工作,但你可能想做一些改变。

// 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;
}

**示例用法**

$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);

你可以一步完成这两件事,但我认为先生成多维数组更简洁。古德勒克!

Saad Imran的解决方案工作正常,除非你想在同一列表中多个导航项。我必须修改几行代码才能生成经过验证的项目列表。我还添加了缩进,使生成的代码更具可读性。我使用空格,但这可以很容易地更改为制表符,如果你喜欢,只需替换4个空格与"'t"。(注意必须使用双引号,而不是单引号,否则PHP不会用TAB字符替换,而是用't)

我在codeigniter 2.1.0中使用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;
}

我正在得到(为了澄清,切换到ol)

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

现在得到

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

生成精美的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>

最简单的方法可能是使用对象。这些可以很容易地操作,也可以很容易地从数据库中获得的数组中创建。

例如,每个对象都有:

  1. ID
  2. 对父对象的对象引用
  3. 子对象列表

您将需要一个静态函数,该函数能够找出具有特定数据库ID的对象。这是在创建新对象期间通过在静态列表中放置引用来完成的。然后可以在运行时调用并检查此列表。

其余部分非常简单:

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

至于返回列表中的对象,最好是递归地完成;

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>'
    }
}

我在同一个项目上工作,我还没有发现需要使用对象。数据库基本上负责结构,php函数可以完成其余的工作。我的解决方案是为指向节名的页面添加父字段,就像您所做的那样,但也为sections表添加父字段,以便section可以作为父部分指向其他section。它仍然非常易于管理,因为只有两个父字段要跟踪,每个表一个,并且我可以在将来根据需要嵌套我的结构。

对于动态树创建,我将递归地检查父元素,直到遇到null,这表明当前元素位于doc根目录。这样我们就不需要知道函数代码中当前页面结构的任何细节,我们可以专注于在mysql中添加和排列页面。由于另一张海报展示了一些菜单html,我想我应该在这里添加一个动态面包屑路径的例子,因为主题是如此相似。

数据
// 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

功能
// 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;
  }
}
使用

// 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 ) );
}

输出

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