【问题标题】:PHP Recursive menu functionPHP递归菜单功能
【发布时间】:2013-11-05 04:37:34
【问题描述】:

我的情况和这个帖子很相似

PHP: nested menu with a recursive function, expand only some nodes (not all the tree)

我需要一些帮助...

这是我的 $menuJSONArray 变量的(部分)内容(在调用函数时使用):

    Array
(
[0] => Array
    (
        [Menu_IDX] => 1
        [Order] => 1
        [Name] => History
        [Parent] => 
        [Path] => History
        [Link] => 
    )

[1] => Array
    (
        [Menu_IDX] => 2
        [Order] => 25
        [Name] => Review
        [Parent] => 
        [Path] => Review
        [Link] => Review
    )

[2] => Array
    (
        [Menu_IDX] => 3
        [Order] => 35
        [Name] => Past Medical History
        [Parent] => 
        [Path] => Past Medical History
        [Link] => Past Medical History
    )
 [3] => Array
    (
        [Menu_IDX] => 4
        [Order] => 45
        [Name] => Item 1
        [Parent] => 0
        [Path] => Item 1
        [Link] => Item 1
    )
 [4] => Array
    (
        [Menu_IDX] => 5
        [Order] => 55
        [Name] => Item 2
        [Parent] => 0
        [Path] => Item 2
        [Link] => Item 2
    )
 [5] => Array
    (
        [Menu_IDX] => 6
        [Order] => 65
        [Name] => Item 3
        [Parent] => 0
        [Path] => Item 3
        [Link] => Item 3
    )
)

等等……

我正在使用下面的函数,但我一直卡在第一项的循环中。真的需要一些帮助来让我的大脑围绕这个功能。提前谢谢大家,这个社区太棒了!!

///RECURSIVE MENU
function recursive($parent, $array) {
$has_children = false; //set value of has children to false by default
foreach($array as $key => $value) {  //loop through the array as key-value pairs
    if ($value['Parent'] == $parent) {  //if the value of Parent field is equal to parent variable   
        if ($has_children === false && $parent) {  //if children is false but parent is not null, this is a sub item
            $has_children = true;  //children is true
            echo '<ul>' ."\n";  //create parent menu ul
        } //otherwise just create the item
        echo '<li>' . "\n";  //create li for parent menu item
        echo '<a href="'.$value['Path'].'">' . $value['Name'] . '</a>' . " \n";  //create link for menu item
            recursive($key, $array); //create sub menu
        echo "</li>\n"; //end parent menu item
    }
}
if ($has_children === true && $parent) echo "</ul>\n"; //end parent menu
}

?>
<?php echo recursive(0, $menuJSONArray); ?></ul>

我得到的回报是:

<ul>



<li>
<a href="History">History</a> 
<li>
<a href="History">History</a> 
<li>
<a href="History">History</a> 
<li>
<a href="History">History</a> 
<li>
<a href="History">History</a> 
<li>
<a href="History">History</a>

似乎无法摆脱这个循环。谢谢!!

附:我不关心引用帖子中树的折叠等,因为我将使用 jquery 和 css 处理它。我就是无法正确输出菜单语法。

【问题讨论】:

  • 就个人而言,为什么不在数组中使用额外的级别来表示父/子关系:[{"name":"Top Level","path":"/path","children":[{"name":"child","path":"/path/child"}]},{"name":"No Children","path":"/childless"}]
  • 在递归函数中使用循环可能很危险。循环是递归的本质,因此从循环内部调用递归函数可能不会产生您期望的结果。您还使用foreach 循环作为键/值对来迭代具有数字索引的数组,这是有问题的,因为当您从循环中调用递归函数时,您不会增加/更改数组键被传递到递归调用中,因此您将相同的数组节点传递到函数中,从而形成无限循环。
  • @Tim Withers:我想过这个,但是,我正在构建的数组来自数据库(不是 SQL,否则这很容易),我想防止不得不更改客户端的数据库结构。不过感谢您的意见。
  • @KeonRamezani:好的,这很有帮助。我将不得不考虑一下。递归往往会让我的大脑陷入循环!我之前在最后有一个增量,我得到了相同的结果,所以我知道我没有正确实现它。首先的想法是重新形成 foreach 删除键值,这是有道理的,为什么不会在那里产生正确的结果。
  • 您可以查看此评论。 stackoverflow.com/questions/23916237/…

标签: php recursion multidimensional-array menu


【解决方案1】:

我同意@Tim Withers 的观​​点,我从准备当前数组开始解决问题:

function prepareMenu($array)
{
  $return = array();
  //1
  krsort($array);
  foreach ($array as $k => &$item)
  {
    if (is_numeric($item['Parent']))
    {
      $parent = $item['Parent'];
      if (empty($array[$parent]['Childs']))
      {
        $array[$parent]['Childs'] = array();
      }
      //2
      array_unshift($array[$parent]['Childs'],$item);
      unset($array[$k]);
    }
  }
  //3
  ksort($array);
  return $array;
}

一些解释。

  1. 这是一个弱点,因为我假设您的菜单数组的顺序是不变的。假设顺序为:
    • 顶部元素优先
    • 之后的孩子
    • 之后的孩子们的孩子们
    • 等等..
  2. 这是我在数组开头添加一个子元素以保存原始顺序的地方。
  3. 回滚到原始订单。

然后函数构建菜单:

function buildMenu($array)
{
  echo '<ul>';
  foreach ($array as $item)
  {
    echo '<li>';
    echo $item['Name'];
    if (!empty($item['Childs']))
    {
      buildMenu($item['Childs']);
    }
    echo '</li>';
  }
  echo '</ul>';
}

有了这个和正确的数组顺序,无论兔子洞有多深 - 你都有你的树。

用法:

$menu = prepareMenu($menu);
buildMenu($menu);

当然……肯定有更好的办法……:-P


编辑:

对于数组(稍加修饰 [next child]):

$menu = array(
array(
        'Menu_IDX' => '1',
        'Order' => '1',
        'Name' => 'History',
        'Parent' => '',
        'Path' => 'History',
        'Link' => '',
    ),
array
    (
        'Menu_IDX' => '2',
        'Order' => '25',
        'Name' => 'Review',
        'Parent' => '',
        'Path' => 'Review',
        'Link' => 'Review',
    ),
array
    (
        'Menu_IDX' => '3',
        'Order' => '35',
        'Name' => 'Past Medical History',
        'Parent' => '',
        'Path' => 'Past Medical History',
        'Link' => 'Past Medical History',
    ),
array
    (
        'Menu_IDX' => '4',
        'Order' => '45',
        'Name' => 'Item 1',
        'Parent' => '0',
        'Path' => 'Item 1',
        'Link' => 'Item 1',
    ),
array
    (
        'Menu_IDX' => '5',
        'Order' => '55',
        'Name' => 'Item 2',
        'Parent' => '0',
        'Path' => 'Item 2',
        'Link' => 'Item 2',
    ),
array
    (
        'Menu_IDX' => '6',
        'Order' => '65',
        'Name' => 'Item 3',
        'Parent' => '0',
        'Path' => 'Item 3',
        'Link' => 'Item 3',
    ),
array
    (
        'Menu_IDX' => '7',
        'Order' => '65',
        'Name' => 'Item 31',
        'Parent' => '5',
        'Path' => 'Item 31',
        'Link' => 'Item 31',
    )
);

输出将是:

  • 历史
    • 项目 1
    • 第 2 项
    • 项目 3
      • 项目 31
    • 审核
    • 既往病史

【讨论】:

  • 感谢您的回复。它让我更进一步。我现在遇到的主要问题是“儿童”声明。我有一个平面数组,只有“父”ID 来显示关系(参见原始帖子中的数组)。如何将其放入多维数组中,包括“子”键中每个父数组下每个子数组的数组?这是让我现在很难过的部分,似乎需要另一个递归函数来做到这一点。再次感谢您的帮助!
  • 第一个函数prepareMenu 将您的原始数组准备为多维。然后buildMenu 打印它。 buildMenu 我认为更像是伪递归函数。您可以复制和粘贴我的代码并将其与原始数组一起使用 - 它可以工作,但不是可选的。我会在我的答案中添加输出。
  • 我第一次实施您的解决方案时一定有什么问题。它现在运行良好!非常感谢您的帮助,也感谢其他所有人。
  • 这个功能很漂亮,非常感谢。
【解决方案2】:

你的问题很详细,很好。

查看代码,我看到的第一个问题是:

if ($value['Parent'] == $parent) {

第一次进入foreach循环,$value['Parent']NULL$parent0

因为您正在进行== 比较,所以计算结果为TRUE。试试吧:

if ($value['Parent'] === $parent) {

注意===。这也会检查数据类型并要求它也匹配。

第二个问题将与该行有关:

if ($has_children === false && $parent) {  //if children is false but parent is not null, this is a sub item

注释清楚地表明您要检查 $parent 是否不为空,但您没有这样做,您将 $parent 的整数值转换为布尔值,因此 0 将被处理如FALSE,其他为TRUE

试试吧:

if ($has_children === false && !is_null($parent)) {  //if children is false but parent is not null, this is a sub item.

【讨论】:

  • 感谢您的意见,我尝试了您的建议,但无法获得嵌套在父项下的子项。嗯......我还在努力解决它
猜你喜欢
  • 2012-06-02
  • 2013-06-13
  • 2018-08-28
  • 2010-10-24
  • 2010-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-11
相关资源
最近更新 更多