Php 将BOM表结构扁平化为层次结构,检测树/图中的周期

Php 将BOM表结构扁平化为层次结构,检测树/图中的周期,php,hierarchy,hierarchical-data,hierarchical,flat,Php,Hierarchy,Hierarchical Data,Hierarchical,Flat,我有许多存储物料清单层次结构数据的表,如: id | childId a | b a | c c|d d|a 我需要找到闭环,比如在本例中,d指向a,这会破坏系统,因为一些递归函数永远不会停止 我的想法是将此数据结构从平面转换为层次结构,然后使用递归迭代层次结构 我正在网上查阅一些知识,以及将平面结构转换为嵌套结构的大多数解决方案,包括以下情况: id | parentId 结构,其中顶级项的父ID设置为根值(例如零) 我是一名自学成才的系统开发人员多年,但现在我缺乏你们许多人在学习期间所受的教

我有许多存储物料清单层次结构数据的表,如:

id | childId

a | b

a | c

c|d

d|a

我需要找到闭环,比如在本例中,
d
指向
a
,这会破坏系统,因为一些递归函数永远不会停止

我的想法是将此数据结构从平面转换为层次结构,然后使用递归迭代层次结构

我正在网上查阅一些知识,以及将平面结构转换为嵌套结构的大多数解决方案,包括以下情况:

id | parentId

结构,其中顶级项的父ID设置为根值(例如零)

我是一名自学成才的系统开发人员多年,但现在我缺乏你们许多人在学习期间所受的教育:)

编辑:图形示例

Valid trees:
a)
      A              
     / \             
    B   C               
         \
          D
         / \
        E   F

b)

      K -> E
     / \
    G   B
   /|\ 
  H I J


Invalid tree (closed loop)
      A             
     / \ 
    B   C<----\
         \     |
          D   /
         / \ /
        E   F
             \
              G
             /|\
            H I J
有效树:
(a)
A.
/ \             
B C
\
D
/ \
EF
(b)
K->E
/ \
G B
/|\ 
HIJ
无效树(闭环)
A.
/ \ 

经过一点尝试和错误之后,这里是我想到的

给定关系的平面列表,它构建查找,然后在递归遍历函数中使用查找。遍历函数“记住”请求的原始零件(
$initialPart
),并在检测到循环时引发异常。 例如,异常:
检测到循环:f=>c=>d=>f

脚本:

$rows = [
    ['assemblyId' => 'a', 'partId' => 'b'],
    ['assemblyId' => 'a', 'partId' => 'c'],
    ['assemblyId' => 'c', 'partId' => 'd'],
    ['assemblyId' => 'd', 'partId' => 'e'],
    ['assemblyId' => 'd', 'partId' => 'f'],
    ['assemblyId' => 'f', 'partId' => 'c'],
    ['assemblyId' => 'f', 'partId' => 'g'],
    ['assemblyId' => 'g', 'partId' => 'h'],
    ['assemblyId' => 'g', 'partId' => 'i'],
    ['assemblyId' => 'g', 'partId' => 'j'],
];

$assemblyLookup = [];
foreach ($rows as $row) {
    $assemblyLookup[$row['assemblyId']][$row['partId']] = $row;
}

function getTree(&$part, $initialPart = null, $path = '', $level = 0, $maxLevel = 100) {
    global $assemblyLookup;

    if ($initialPart === null) {
        $initialPart = $part;
    }

    if ($level >= $maxLevel) {
        return;
    }

    $path .= '=>' . $part['partId'];
    if (is_array($assemblyLookup[$part['partId']])) {
        $lookup = $assemblyLookup[$part['partId']];
        foreach ($lookup as $child) {

            if ($child['partId'] === $initialPart['partId']) {
                $circularPath = substr($path, 2) . '=>' . $child['partId'] . '!';
                throw new Exception("Loop detected: " . $circularPath);
                return;
            }

            $part['children'][$child['partId']] = $child;
            getTree($part['children'][$child['partId']], $initialPart, $path, ++$level, $maxLevel);
        }
    } else {
        $path .= ';';
        $part['path'] = substr($path, 2); // Strip arrow from left side
    }

    return $part;

}

$part = ['partId' => 'f'];

$tree = getTree($part);

的确很有趣,但在我的例子中,它不是一个链表,而是一棵树。