You want to save each element in the DB with an ID and a parentID (that can be null if no such parent exists). The PHP is your "biggest" problem, but references are your huge friend here for turning a flat structure into a tree structure.
Consider the following DB result:
----------------------------
| id | parentID | text |
|----|----------|----------|
| 1 | null | Item #1 |
| 2 | 5 | Item #2 |
| 3 | 2 | Item #3 |
| 4 | 2 | Item #4 |
| 5 | null | Item #5 |
| 6 | 5 | Item #6 |
| 7 | 3 | Item #7 |
| 8 | 5 | Item #8 |
| 9 | 1 | Item #9 |
| 10 | 7 | Item #10 |
----------------------------
Consider the following array (that could be from a DB result - it's important that the ID is the key, though. You can simply transform your DB result into something like the following (the only needed key is "parentID"):
$menu = array(
1 => array('text' => 'Item #1', 'parentID' => null),
2 => array('text' => 'Item #2', 'parentID' => 5),
3 => array('text' => 'Item #3', 'parentID' => 2),
4 => array('text' => 'Item #4', 'parentID' => 2),
5 => array('text' => 'Item #5', 'parentID' => null),
6 => array('text' => 'Item #6', 'parentID' => 5),
7 => array('text' => 'Item #7', 'parentID' => 3),
8 => array('text' => 'Item #8', 'parentID' => 5),
9 => array('text' => 'Item #9', 'parentID' => 1),
10 => array('text' => 'Item #10', 'parentID' => 7),
);
And to turn it into a tree structure:
<?php
$addedAsChildren = array();
foreach ($menu as $id => &$menuItem) { // note that we use a reference so we don't duplicate the array
if (!empty($menuItem['parentID'])) {
$addedAsChildren[] = $id; // it should be removed from root, but we'll do that later
if (!isset($menu[$menuItem['parentID']]['children'])) {
$menu[$menuItem['parentID']]['children'] = array($id => &$menuItem); // & means we use the REFERENCE
} else {
$menu[$menuItem['parentID']]['children'][$id] = &$menuItem; // & means we use the REFERENCE
}
}
unset($menuItem['parentID']); // we don't need parentID any more
}
unset($menuItem); // unset the reference
foreach ($addedAsChildren as $itemID) {
unset($menu[$itemID]); // remove it from root so it's only in the ['children'] subarray
}
With this new array we can use a simply recursive function to output it all in a ul..li sense:
echo makeTree($menu);
function makeTree($menu) {
$tree = '<ul>';
foreach ($menu as $id => $menuItem) {
$tree .= '<li>' . $menuItem['text'];
if (!empty($menuItem['children'])) {
$tree .= makeTree($menuItem['children']);
}
$tree .= '</li>';
}
return $tree . '</ul>';
}
Resulting in:
<ul><li>Item #1<ul><li>Item #9</li></ul></li><li>Item #5<ul><li>Item #2<ul><li>Item #3<ul><li>Item #7<ul><li>Item #10</li></ul></li></ul></li><li>Item #4</li></ul></li><li>Item #6</li><li>Item #8</li></ul></li></ul>
..and rendered:

DEMO