рекурсия — (PHP) Совокупный подсчет детей в многомерном массиве, заполняемых в рекурсивной функции

У меня есть таблица в базе данных MySQL, которая представляет генетическое древо языка. Каждая строка является языком с идентификатором и родительским идентификатором; например:

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

Моя цель состоит в том, чтобы превратить эти строки в довольно вложенный список HTML с совокупными счетчиками после каждого языка, в котором есть какие-либо дочерние языки, с помощью простой рекурсивной функции PHP, в результате чего получается что-то вроде этого (на основе языков в приведенных выше строках примера):

  • Протоиндоевропейский (11)
    • Кельтский (6)
      • Островной кельтский (5)
        • Goidelic (2)
          • ирландский
          • с острова Мэн
        • Британский (1)
          • валлийский
    • Германский (4)
      • Западногерманский (3)
        • Англосаксонский (2)
          • Английский (1)
            • Древнеанглийский

У меня есть следующая функция 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 элемент каждого массива.

Тем не менее, я просто не могу ради своей жизни, несмотря на много головокружительной работы, поиска в Google и просмотра множества вопросов рекурсии, подсчета агрегации и подсчета массивов здесь, на SO, найти способ создания счетчиков, которые описывают количество потомков языков для каждого языка.

Что бы я ни делал, где бы я ни создавал, изменял и использовал переменные счетчика и что бы я ни пытался считать (независимо от того, выполняю ли я $ count ++ каждый раз, когда я выполняю итерации по языку, делаю ‘count ($ children)’ и т. Д. ), Я всегда получаю результаты, в которых счетчик не сбрасывается, когда функция достигает «более высокого» уровня, так что вместо этого я получаю списки:

  • Протоиндоевропейский (12)
    • Кельтский (6)
      • Островной кельтский (5)
        • Goidelic (2)
          • ирландский
          • с острова Мэн
        • Британский (4)
          • валлийский
    • Германский (9)
      • Западногерманский (12)
        • Англосаксонский (14)
          • Английский (15)
            • Древнеанглийский

— Или что-то в этом роде — числа и способы их суммирования различаются в разных реализациях, которые я пытался применить; но у всех них есть общее, что счетчик продолжает увеличиваться повсюду, никогда не сбрасываясь. Или, в качестве альтернативы, если я попытаюсь сбросить его в какой-то момент, естественно, он будет сброшен на каждый итерации, оставив меня с:

  • Протоиндоевропейский (2)
    • Кельтский (2)
      • Островной кельтский (1)
        • Goidelic (2)
          • ирландский
          • с острова Мэн
        • Британский (1)
          • валлийский
    • Германский (1)
      • Западногерманский (1)
        • Англосаксонский (1)
          • Английский (1)
            • Древнеанглийский

Понятно, логика здесь не моя сильная сторона.

Может ли кто-то помочь моему мозгу самопроизвольно сгореть и / или взорваться, предложив мне способ достичь того типа «интеллектуального подсчета», который я ищу здесь?

-1

Решение

Думая об этом, вместо того, чтобы выполнять миллион рекурсивных запросов в 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>';
}
1

Другие решения

Других решений пока нет …