我对这个问题感到有点困惑,我已经考虑了一段时间了。我的数据库中有一个表可以执行任务。通过在parent_id字段中保存主键,每个任务都可以拥有父任务。我对这些任务的联系深度没有限制。
+-----------+-------+-----+
| Field | Type | Key |
+-----------+-------+-----+
| id | int | PRI |
| parent_id | int | MUL |
+-------------------+-----+
没有parent_id的任务是“项目”,并且可以通过共享父任务将所有任务分组到任务组中。我现在想用一个HTML选择框填充项目的所有后代。
Task 1
-Task 1.1
-Task 1.2
-Task 1.2.1
-Task 1.2.2
-Task 1.3
Task 2
我该如何解决这个问题?我认为某种递归函数是有序的,但我似乎无法真正想出如何去做。
任何帮助都会得到很大的帮助。 :)
答案 0 :(得分:6)
我强烈建议您阅读有关storing hierarchical data in a database的文章。这里讨论了两种算法,根据您的需要,其中任何一种算法都是合适的。
邻接列表模型
这就是你现在拥有的。树的每个节点都存储对其父级的引用,您可以通过选择树的每个级别并迭代节点来递归地确定节点的路径。这很容易实现,但缺点是要确定节点的特定路径,需要递归查询。如果您的树受到很多更改(即写入),这是一个很好的方法,因为动态查找每个节点可以很好地适应不断变化的树。如果它读得很重,那么你在递归中会有一些开销。
修改预订树遍历
我最喜欢的,这是一个非常简洁的算法。您可以存储对每个给定节点的“左”和“右”节点的引用,而不是存储对父级的引用(为了方便起见,您可以执行此操作)。可以在单个选择查询中确定节点的整个路径,或者相反,可以在节点的所有子节点中确定。该算法难以实现,但它对读取繁重的树具有性能优势。缺点是每次移动或添加节点时,都必须重新计算树的整个分支,因此它可能不适合写入大量数据集。
无论如何,希望这篇文章能给你一些想法。这是一个很好的。
答案 1 :(得分:1)
这是一个如何递归遍历数据库以设置HTML表单的示例。它是zombat所谓的“邻接列表模型”的实现。
它使用两个函数:一个只是为了获得“顶级”元素(项目);和一个递归,以获得给定元素的所有子元素。然后我用它来填充HTML表单。
<?php
/**
* Fetches all the projects and returns them as an array.
* "Projects" meaning: tasks without a parent.
* @return array
*/
function getProjects() {
$sql = "SELECT id FROM tree WHERE parentID IS NULL";
$result = mysql_query($sql) or die(mysql_error());
$results = array();
while($row = mysql_fetch_assoc($result)) {
$results[] = $row['id'];
}
return $results;
}
/**
* Fetches all tasks belonging to a specific parent.
* Adds HTML space entities to represent the depth of each item in the tree.
* @param int $parent_id The ID of the parent.
* @param array $data An array containing the dat, filled in by the function.
* @param int $current_depth Indicates the current depth of the recursion.
* @return void
*/
function getTasks($parent_id, &$data, $current_depth=1) {
$sql = "SELECT id FROM tree WHERE parentID = {$parent_id}";
$result = mysql_query($sql) or die(mysql_error());
while($row = mysql_fetch_assoc($result)) {
$data[] = str_repeat(' ', $current_depth) . '- ' . $row['id'];
getTasks($row['id'], $data, $current_depth + 1);
}
}
/*
* Fetch all the data and set it up so it can be used in the HTML
*/
mysql_connect('localhost', 'usr', 'pwd');
mysql_select_db('test');
// Get all the projects, adding a "-" as the initial value of the box.
$projects = array_merge(array('-'), getProjects());
// Fetch the tasks.
// If no project has been selected, just show a "please select"
$tasks = array();
if(isset($_GET['project']) && $_GET['project'] != '-') {
getTasks($_GET['project'], $tasks);
}
else {
$tasks = array('Select a project');
}
mysql_close();
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Tree Select Example</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
<select name="project" onchange="this.parentNode.submit();">
<?php
foreach($projects as $_project) {
$selected = ($_project == @$_GET['project']) ? ' selected' : '';
echo "<option value=\"{$_project}\"{$selected}>{$_project}</option>";
}
?>
</select><br>
<select name="tasks[]" multiple size="10">
<?php
foreach($tasks as $_task) {
echo "<option value=\"{$_task}\">{$_task}</option>";
}
?>
</select><br>
<input type="submit">
</form>
<pre><?php print_r($_GET); ?></pre>
</body>
</html>
答案 2 :(得分:0)
请让此功能与您的虚线 - 指示器进行树视图。我为选择框选项做了。
function display_children($parent, $level) {
// retrieve all children of $parent
$output = "";
$result = mysql_query('SELECT * FROM treeview_items WHERE parent_id="'.$parent.'";');
while ($row = mysql_fetch_array($result)) {
echo "<option value='".$row['id']."'>".str_repeat('--',$level).$row['name']."</option>" ."<br>";
display_children($row['id'], $level+1);
}
}