У меня есть таблица в базе данных 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)
- Древнеанглийский
Понятно, логика здесь не моя сильная сторона.
Может ли кто-то помочь моему мозгу самопроизвольно сгореть и / или взорваться, предложив мне способ достичь того типа «интеллектуального подсчета», который я ищу здесь?
Думая об этом, вместо того, чтобы выполнять миллион рекурсивных запросов в 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>';
}
Других решений пока нет …