2010-01-18 101 views
4

我對這個問題有點難住,現在我一直在想它。我在我的數據庫中有一張桌子,裏面有一個任務。每個任務都可以通過在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 

我該怎麼辦?我認爲某種遞歸函數是有序的,但我似乎無法真正瞭解如何去做。

任何幫助將大大apprecaited。 :)

+0

谷歌「物化路徑」:-) – prodigitalson 2010-01-18 17:35:07

回答

6

我強烈建議你閱讀這篇關於storing hierarchical data in a database的文章。這裏討論了兩種算法,根據你的需要,它們中的任何一個都可能是合適的。

鄰接表型號

這是您目前擁有。樹的每個節點都存儲對其父項的引用,並且可以通過選擇樹的每個級別並遍歷節點來遞歸確定節點的路徑。這很容易實現,但缺點是要確定到節點的特定路徑,需要遞歸查詢。如果你的樹受到很多變化(即寫入)的影響,這是一個好方法,因爲動態地找到每個節點對於不斷變化的樹來說工作得很好。如果它是重讀的,你在遞歸中會有一些開銷。

改性預購樹的遍歷

我最喜歡的,這是一個非常整潔的算法。除了存儲對父節點的引用(爲方便起見您可以執行此操作),您將爲每個給定節點存儲對「左」和「右」節點的引用。節點的整個路徑可以在單個選擇查詢中確定,或者相反地,可以在節點的所有子節點中確定。該算法實施起來比較困難,但它對於讀取繁重的樹木具有性能優勢。缺點是每次移動或添加節點時,都必須重新計算樹的整個分支,因此它可能不適合寫入重度數據集。

無論如何,希望文章給你一些想法。這是一個很好的。

+0

謝謝。預定樹遍歷對實現會很有趣,但是我現在在這個項目中轉換得太遠了。我修改了該頁面上提供的代碼,以便充分填充選擇範圍。 – Gazillion 2010-01-18 19:40:28

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('&nbsp;', $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> 
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); 
    } 
}