【问题标题】:How to convert this MPTT array into a tree structure in PHP?如何在 PHP 中将此 MPTT 数组转换为树形结构?
【发布时间】:2023-04-03 12:40:02
【问题描述】:

我在数据库中有分层数据,以Modified Preorder Tree Traversal 格式存储。我在查询中提取数据,该查询类似于“SELECT ID、LeftRight、名称等 FROM Table ORDER BY Left;”。我正在尝试将此数据从 DB 提供给我的平面数组转换为树结构,然后我将使用 PHP 的 json_encode 函数将其输出为 JSON。

不过,我无法让我的树结构代码在第一级之外工作。这是一个最小的测试用例:

<pre><?php

function projectListToTree($projects) {
    $stack = Array();
    for($x =0; $x < count($projects); $x++) {
        $project = $projects[$x];
        $project['Children'] = Array();

        while(count($stack) > 0 && $stack[count($stack) - 1]['Right'] < $project['Right']) {
            array_pop($stack);
        }

        if(count($stack) > 0) {
            $stack[count($stack) - 1]['Children'][] = $project; 
            echo "Adding " . $project['Name'] . " to " . $stack[count($stack) - 1]['Name'] . " for a total of "
                . count($stack[count($stack) - 1]['Children']) . " kids\n";
        } else {
            echo "No parent\n"; 
        }

        echo "stack count: " . count($stack) . "\n";

        array_push($stack, $project);
    }

    echo "Left in stack: " . count($stack) . "\n";

    return $stack[0];
}

/*
This is basically what comes from the DB.
Should be:
  Parent
    First Child
    Second Child
      Grand Child
*/
$projects = Array(
    Array(
        "ID" => "2",
        "Left" => "2",
        "Right" => "9",
        "ParentID" => "1",
        "Name" => "Parent"
    ),
    Array(
        "ID" => "3",
        "Left" => "3",
        "Right" => "4",
        "ParentID" => "2",
        "Name" => "First Child"
    ),
    Array(
        "ID" => "4",
        "Left" => "5",
        "Right" => "8",
        "ParentID" => "2",
        "Name" => "Second Child"
    ),
    Array(
        "ID" => "5",
        "Left" => "6",
        "Right" => "7",
        "ParentID" => "4",
        "Name" => "Grand Child"
    )
);


$tree = projectListToTree($projects);
echo "-----\n\n\n\n";
var_dump($tree);

?></pre>

这就是我得到的输出:

No parent
stack count: 0
Adding First Child to Parent for a total of 1 kids
stack count: 1
Adding Second Child to Parent for a total of 2 kids
stack count: 1
Adding Grand Child to Second Child for a total of 1 kids
stack count: 2
Left in stack: 3
-----



array(6) {
  ["ID"]=>
  string(1) "2"
  ["Left"]=>
  string(1) "2"
  ["Right"]=>
  string(1) "9"
  ["ParentID"]=>
  string(1) "1"
  ["Name"]=>
  string(6) "Parent"
  ["Children"]=>
  array(2) {
    [0]=>
    array(6) {
      ["ID"]=>
      string(1) "3"
      ["Left"]=>
      string(1) "3"
      ["Right"]=>
      string(1) "4"
      ["ParentID"]=>
      string(1) "2"
      ["Name"]=>
      string(11) "First Child"
      ["Children"]=>
      array(0) {
      }
    }
    [1]=>
    array(6) {
      ["ID"]=>
      string(1) "4"
      ["Left"]=>
      string(1) "5"
      ["Right"]=>
      string(1) "8"
      ["ParentID"]=>
      string(1) "2"
      ["Name"]=>
      string(12) "Second Child"
      ["Children"]=>
      array(0) {
      }
    }
  }
}

如您所见,“Grandchild”在某个地方迷路了,尽管 projectListToTree 函数中的输出似乎表明它应该在那里。似乎我扔给它的任何树结构都会将任何东西都放在第二层以下。对可能发生的事情有任何见解吗?

谢谢!

【问题讨论】:

    标签: php arrays tree mptt


    【解决方案1】:

    问题是分配数组不会复制引用,而是复制数组。这意味着,您在“父”节点的“子”中拥有的“第二个子”数组与您将“孙子”添加到的数组不同,而是它的副本。

    要解决此问题,您必须明确使用引用分配而不是复制:

    function projectListToTree($projects) {
        $stack = Array();
        for($x =0; $x < count($projects); $x++) {
            $project = &$projects[$x];
            $project['Children'] = array();
    
            while(count($stack) > 0 && $stack[count($stack) - 1]['Right'] < $project['Right']) {
                    array_pop($stack);
            }
    
            if(count($stack) > 0) {
                    $stack[count($stack) - 1]['Children'][] = &$project; 
    
                    echo "Adding " . $project['Name'] . " to " . $stack[count($stack) - 1]['Name'] . " for a total of "
                            . count($stack[count($stack) - 1]['Children']) . " kids\n";
    
                    echo "\n";
            } else {
                    echo "No parent\n"; 
            }
    
            echo "stack count: " . count($stack) . "\n";
    
            $stack[] = &$project;
        }
    
        echo "Left in stack: " . count($stack) . "\n";
    
        return $stack[0];
    }
    

    请注意,在三个位置添加了一个 & 符号。

    由于这个问题,在 php 中使用嵌套数组和赋值运算符时必须非常小心。

    这也意味着在嵌套数组中使用大量数据时,处理器使用率和内存占用量会很大。比如上面的例子中,当projectListToTree()返回时,完整的数组树被复制到局部变量$tree中,并且(因为php垃圾回收器很烂)两次在内存中。

    【讨论】:

    • 谢谢!我想我仍然习惯于将数组视为对象并通过引用进行分配的语言。这工作得很好,除了关于 array_push($stack, &$project) 行的警告(调用时传递引用已被弃用),我通过将其更改为 $stack[] = &$project; .
    • 我已更改答案中的代码以规避警告。
    【解决方案2】:

    您是否在调用 array_pop() 时添加了 echo 语句?从未经测试的情况下阅读本文,我认为您正在将记录从堆栈中弹出并扔掉。

    【讨论】:

      猜你喜欢
      • 2022-01-01
      • 2018-08-03
      • 1970-01-01
      • 2019-04-04
      • 2014-10-13
      • 2021-08-07
      • 1970-01-01
      • 2018-04-19
      • 1970-01-01
      相关资源
      最近更新 更多