I have some hierarchical data that I need to display in a series of nested UL's. For each item I have a name, an ID, and a depth value. Normally I would just group these items by depth, but I actually need to create a tree structure with my data, like this:
Here is my question: is there a good way to generate valid markup (I would love it if I could make it print out with proper tabbing too, but that will be tough) where my data will be wrapped in nested UL's? I already have a solution that kinda works, but I am getting a single stray tag. Here is the code I have for that:
<?php
include("includes/classes/Database.class.php");
$db = new Database();
$query = "SELECT COUNT(parent.Name) - 2 as level, node.Name AS Name, node.ID
FROM Region AS node, Region AS parent
WHERE node.LeftVal BETWEEN parent.LeftVal AND parent.RightVal and node.Name <> 'Earth'
GROUP BY node.ID
ORDER BY node.LeftVal";
$results = $db->executeQuery($query);
?>
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<?php
$last_level = 0;
?>
<ul id="regionTree">
<?php
while ($row = mysql_fetch_assoc($results)) {
$link = '<li>'.PHP_EOL.'<a href="addChild.php?parentid='.$row["ID"].'">'.$row["Name"]."</a>".PHP_EOL;
$diff = $last_level - $row["level"];
if($diff == 0){
// Sibling
echo ($row["level"] != 0) ? '</li>'.PHP_EOL.$link:$link;
}
elseif($diff < 0){
// Child
$demoter = '<ul>'.PHP_EOL;
for ($i=0; $i > $diff; $i--) {
echo $demoter;
}
echo $link;
}
else{
// Parent
$promoter = '</li>'.PHP_EOL.'</ul>';
for ($i=0; $i < $diff; $i++) {
echo ($row["level"] != 0) ? $promoter.PHP_EOL."</li>":$promoter;
}
echo $link;
}
$last_level = $row["level"];
}
?>
</li>
</ul>
</body>
</html>
Any Ideas?
::Edit:: I have created a pastebin with the generated source which does not validate. Pastebin.com
::EDIT 2:: Here is the schema for the Region table. It is designed using a hybrid of the nested set model and the adjacency list model.
CREATE TABLE Region (
ID INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Stores the ID for the Region.',
Name VARCHAR(45) NOT NULL COMMENT 'Stores the name of the Region',
Region_Type VARCHAR(45) NOT NULL COMMENT 'Stores the Region type.',
Parent INT COMMENT 'Stores the ID of the Parent Region',
LeftVal INT NOT NULL,
RightVal INT NOT NULL,
PRIMARY KEY (ID)
) COMMENT 'Stores information about all Regions.' ENGINE=INNODB
ROW_FORMAT=DEFAULT CHARACTER SET utf8 collate utf8_general_ci;