(PHP) 在递归函数中填充的多维数组中子项的聚合计数

(PHP) Aggregate counting of children in multidimensional array being populated in recursive function

我在 MySQL 数据库中有一个 table 表示遗传语言树。每一行都是一种带有 ID 和父 ID 的语言;例如:

id   | language            | parent
-----+---------------------+--------
1    | Proto-Indo-European | NULL
6    | Celtic              | 1
8    | Insular Celtic      | 6
9    | Goidelic            | 8
14   | Irish               | 9
16   | Manx                | 9
21   | British             | 8
22   | Welsh               | 21
109  | Germanic            | 1
115  | West Germanic       | 109
117  | Anglo-Saxon         | 115
118  | Anglic              | 117
119  | Old English         | 118

我的目标是使用一个简单的递归 PHP 函数,将这些行变成一个漂亮的嵌套 HTML 列表,在每种具有任何子语言的语言之后带有聚合计数器,从而产生类似这样的结果(基于上面示例行中的语言):

  • Proto-Indo-European (11)
    • Celtic (6)
      • Insular Celtic (5)
        • Goidelic (2)
          • Irish
          • Manx
        • British (1)
          • Welsh
    • Germanic (4)
      • West Germanic (3)
        • Anglo-Saxon (2)
          • Anglic (1)
            • Old English

我有以下 PHP 函数(此处进行了简化),它创建了一个具有适当结构的多维数组:

function select_languages_hierarchical($parent = NULL) {
    global $db;
    $branch = array();
    $query = "SELECT * FROM languages WHERE parent = $parent";

    if ($q = $db->query($query)) {
        while ($row = $q->fetch_assoc()) {
            $element['id'] = $row['id'];
            $element['name'] = $row['language'];
            $children = select_languages_hierarchical($row['id']);

            if ($children) {
                $element['children'] = $children;
            }

            $branch[] = $element;
        }
    }
    return $branch;
}

这会生成一个与上面的嵌套列表匹配的数组,嵌套数组位于每个数组的 children 元素中。

然而,尽管在 SO 上绞尽脑汁、谷歌搜索和查看了大量的递归、聚合计数和数组计数问题,但我终究无法找到一种方法来创建描述每种语言的后代语言数量的计数器。

无论我做什么,无论我在哪里创建、修改和使用我的计数器变量,无论我尝试计数什么(无论是每次迭代一种语言时做一个ˋ$count++ˋ,做一个ˋcount($children )ˋ, 等), 当函数达到“更高”级别时,我总是得到计数器没有重置的结果,所以我得到这样的列表:

  • Proto-Indo-European (12)
    • Celtic (6)
      • Insular Celtic (5)
        • Goidelic (2)
          • Irish
          • Manx
        • British (4)
          • Welsh
    • Germanic (9)
      • West Germanic (12)
        • Anglo-Saxon (14)
          • Anglic (15)
            • Old English

– 或者类似的东西——在我尝试过的各种不同的实现中,数字和它们相加的方式各不相同;但它们都有一个共同点,即计数器始终在增加,而不会被重置。或者,如果我确实尝试在某个时候重置它,它自然会在 每次 迭代时重置,给我留下:

  • Proto-Indo-European (2)
    • Celtic (2)
      • Insular Celtic (1)
        • Goidelic (2)
          • Irish
          • Manx
        • British (1)
          • Welsh
    • Germanic (1)
      • West Germanic (1)
        • Anglo-Saxon (1)
          • Anglic (1)
            • Old English

显然,逻辑不是我的强项。

有人可以通过建议一种方法让我实现我在这里寻找的那种“智能计数”,从而帮助我的大脑免于自燃 and/or 内爆吗?

考虑一下,而不是 运行 在 PHP 中进行不计其数的递归查询,你可以只做一个基本的 select * 并在 PHP 中构建你的树,然后在那里计数。当然,这只有在您想要整个语言树时才有用。

$lang = array();
$sql = "SELECT * FROM languages";
... run query ...
while($row = fetch results) {
     // store this lang's node
     $lang[$row['id']]['name'] = $row['name']; 

     // add this language to its parent's child array
     $lang[$row['parent']]['children'][$row['id']] = $row['id'];

     // increment its parent's counter
     $lang[$row['parent']]['count']++;
}

现在,这可能会发出一堆关于未定义数组和诸如此类的警告,因为在尝试更新它的计数之前我没有测试父对象的存在。但这只是一些基本的 if (!isset()) { initialize node } 类型的东西。

嵌套 <ul> 的输出将是一个递归函数,但由于您现在已经在树中获得了子节点计数,因此它会简单得多:

function output($id = 1) { // assuming "1" is your tree root node
   echo '<li>' . $lang[$id]['name'] . '(' . $lang[$id]['count'] . ')';
   if ($lang[$id]['count'] > 0) {
       echo '<ul>';
       foreach($lang[$id]['children'] as $childID) {
            output($childID);
       }
       echo '</ul>';
   }
   echo '</li>';
}