如何在PHP中构造一个具有未知子节点的可视化递归树?


How would I best construct a recursive tree visual with unknown child nodes in PHP?

我在开始使用以下内容时遇到了麻烦:

  • 我有一个问题数据库和每个问题的多个答案。
  • 每个答案属于一个问题。
  • 每个答案指向一个(不同的)问题。

我试图在不知道级别数量的情况下创建整个关系路径的多级树可视化。我怎么做才最好?

下面是一个数据数组的例子(简化):

array(
   1 => array( //this key is the answer id
      question_id = 1,
      answers = array(
         0 => answer_opens_question__id = 2,
         1 => answer_opens_question__id = 3,
         2 => answer_opens_question__id = 5
      )
   ),
   2 => array( 
      question_id = 2,
      answers = array(
         0 => answer_opens_question__id = 1,
         1 => answer_opens_question__id = 5
      )
   ),
   5 => array( 
      question_id = 3,
      answers = array(
         0 => answer_opens_question__id = 2
      )
   )
)

理论上,这可能会永远持续下去,并最终进入一个无限循环——尽管这种情况极不可能发生,因为这些数据是用户生成的,而不是动态创建的。

我只是在寻找一种递归显示路径的方法,如question > answer>>question > answer>>question > answer>>question> ... -输出将在HTML中嵌套ul-li

我该怎么做呢?非常感谢您的意见。


编辑/解决方案:

多亏了DaveRandom,我得到了它:写一个函数,循环遍历每个答案,为每个答案调用自己。

function recursiveTree($id) {               
                global $data; //an array like the one above
                if(array_key_exists($id, $data)) {
                    echo '<ul><li>';                        
                    echo $data[$id]['question_name']; //question
                    if(is_array($data[$id]['answers']) && !empty($data[$id]['answers'])) {
                        foreach($data[$id]['answers'] as $answer) {
                            echo '<ul><li class="answerswer"><div>'.$answer['text'].' opens </div>';
                            recursiveTree($answer['answer_opens_question__id']); //call this very function for this answer's question - that's the trick
                            echo '</li></ul>';
                        }
                    }                           
                    echo '<li></ul>';
                }                   
            }//recursiveTree()
recursiveTree(1); //where 1 is the first question's id

我还没有像Dave在这段代码中那样跟踪问题(请查看下面的公认答案)-这将被添加。虽然它已经这样工作了(因为我没有在我的测试数据中有任何循环),但我同意每个人的意见,建议照顾可能的无限循环。

使用li.answer周围的一些填充和一些边框,上面的输出甚至是可读的:)

再次感谢大卫·兰登。

你在数据中有一个循环。问题1的答案指向问题2,问题2的答案指向问题1。#5和#2也一样。

如果你想防止无限循环,你只需要在你向下递归树的时候建立一个并行的面包屑数组。如果您看到问题#2并看到它指向问题#1 -那么,breadcrumb数组表明#1已经输出,因此您跳过递归到该特定分支,并且不会发生循环。

也许这将给你一个正确的方向刺激?(固定)

function recursive_tree ($startQuestionId, $alreadyDisplayed = array()) {
  // Make sure we only display each question once
  $alreadyDisplayed[] = $startQuestionId;
  // Replace this with a sensible query to get the answers for question id $startQuestionId
  $answers = $db->query("SELECT answers.answerId, answers.opensQuestionId FROM questions, answers WHERE questions.questionId = '$startQuestionId' AND answers.questionId = questions.questionId");
  // Echo a header for the question
  echo "<div id='question$startQuestionId'>Question $startQuestionId'n<ul>'n";
  while ($row = $db->fetch()) {
    // Loop through the answers to this question
    echo "<li>'nAnswer id {$row['answerId']} opens question id {$row['opensQuestionId']}'n";
    if (!in_array($row['opensQuestionId'],$alreadyDisplayed)) {
      // The linked question hasn't been displayed, show it here
      $alreadyDisplayed = array_merge($alreadyDisplayed,recursive_tree($row['opensQuestionId'],$alreadyDisplayed));
    } else {
      // The linked question has already been displayed
      echo "(<a href='#question{$row['opensQuestionId']}'>Already displayed</a>)'n";
    }
    echo "</li>'n";
  }
  // Close everything off
  echo "</ul>'n</div>'n";
  return $alreadyDisplayed;
}
// And call it like this
$questionToStartAt = 1;
recursive_tree($questionToStartAt);