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

时间:2011-12-31 02:10:17

标签: php arrays loops parent-child

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

基本上我有一个可以创建页面的系统,当创建页面时,您可以选择父页面进行子导航。在我尝试从数据库生成导航之前,这一切都很好,花花公子。

我不确定某种连接是否会更好但我更喜欢将所有数据放入数组并使用php操作数组。

我的数据库结果如下:

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>

等。 ...

4 个答案:

答案 0 :(得分:12)

我认为你不应该进入对象。另外我认为生成对象等只是额外的工作。在我看来,你应该遍历数组并生成一个代表导航层次结构的多维数组,然后递归循环生成的数组以生成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);

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

答案 1 :(得分:3)

Saad Imran的解决方案可以正常工作,除非你想要在同一个列表中有多个导航项目。我必须更改几行才能生成经过验证的项目列表。我还添加了缩进以使生成的代码更具可读性。我正在使用空格,但如果您愿意,可以轻松更改为标签,只需用"\t"替换4个空格即可。 (注意你必须使用双引号,而不是单引号,否则php不会替换为制表符但实际上是\ 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>

答案 2 :(得分:1)

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

例如,每个对象都有:

  1. 一个ID
  2. 一个名字
  3. 一个链接
  4. 对父对象的对象引用
  5. 子对象列表
  6. 您将需要一个能够找出具有特定数据库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>'
        }
    }
    

答案 3 :(得分:0)

我正在处理同一个项目,但我没有发现需要使用对象。数据库非常关注结构,php函数可以完成其余的工作。我的解决方案是有一个父字段,用于指向部分名称的页面,但也为部分表添加父字段,以便部分可以指向其他部分作为父级。它仍然非常容易管理,因为只有2个父字段可以跟踪,每个表一个,我可以在未来根据需要将我的结构嵌套到多个级别。

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

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

功能

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

输出

sample-page-1.php || test.com&gt;第1页

sample-page-2.php || test.com&gt;你好&gt;第2页

sample-page-3.php || test.com&gt;你好&gt;世界&gt;第3页