简体   繁体   中英

Building an unordered list from 5 column MYSQL table in PHP (trees & recursion)

i've been trying to wrap my head around this for a while now, doing research online yielded nothing. Basically i have a 5 column table representing a tree structure. You could think of it as a directory list. Im trying to build a unordered list menu out of this.

This is the MYSQL table:


Parent/Child/Grandchild/Grand-grandchild/grand-grand-grandchild

France/Aquitaine/Dordogne/Bergerac/Issac

France/Aquitaine/Dordogne/Nontron/Nontron

Cyprus/Paphos/Paphos District/Coral Bay/Coral Bay

Denmark/South Denmark/Langeland/Rudkobing/Langeland

Egypt/Sinai Peninsula/Red Sea/Sharm El Sheikh/Sharm El Sheikh


The unordered list should look like this:

  • France
    • Aquitaine
      • Dordogne
        • Bergerac
          • Isaac
        • Nontron
          • Nontron
  • Cyprus
    • Paphos
      • Paphos District
        • Coral Bay
          • Coral Bay

etc...

Basically producing an unordered list menu representing all five levels, grouping everything correctly.

I have been toying with the following function to try and get what i want: http://kevin.vanzonneveld.net/techblog/article/convert_anything_to_tree_structures_in_php/

could anyone provide any further info on how to solve this? I attempted to build loops for each level but quickly found out this to be futile, and that recursive functions should be used.

Actually, there's no need for recursion here. A tree can be built through repeated usage of an insert function, and that function can be safely implemented in imperative style.

/** 
 * insert($tree,array('a','b','c'),$value) 
 *   ensures that $tree['a']['b']['c'] == $value
 */
function insert(&$tree,$path,$value)
{
  $modified = &$tree;
  foreach ($path as $segment) 
  {
    if (!isset($modified[$segment])) $modified[$segment] = array();
    $modified = & $modified[$segment];
  }
  $modified = $value;  
}

Once you have this, inserting all elements in the tree is fairly easy if your rows are in a format like array('France','Aquitaine','Dordogne','Bergerac','Issac') :

foreach ($rows as $row)
  insert($tree, $row, array());

A possible optimization is to sort the rows lexicographically, which means all rows with a similar prefix will be treated together and thus you can save some traversal time.

Ok, i have managed to cobble together a piece of code which seems to work.

I am manually concancenating columns to /column1/column2/column3/column4/column5 strings in a one dimensional array first; Then applying the functions from url given in question to explode this simple array into an array tree. Finally i make use of the makeULLI function to generate an unordered list, with links for each node present. I have extended the code further to add custom paths to URLs depending how deep the link is, for use in SEO friendly links but i stripped that bit out.

I am attaching the code which does all this and is suprisingly small. The code should work on any number of levels, I have run it on a table with around 400 rows (5 columns) and it executes in 0.0168 seconds. If anyone can see further optimisations to the code i would be grateful.

    foreach ($geoArray as $result)
    {
    $mynewGEO[] = $result['parent'];
    $mynewGEO[] = $result['parent'].'/'.$result['child'];       
    $mynewGEO[] = $result['parent'].'/'.$result['child'].'/'.$result['grandchild'];     
    $mynewGEO[] = $result['parent'].'/'.$result['child'].'/'.$result['grandchild'].'/'.$result['grand-grandchild'];     
    $mynewGEO[] = $result['parent'].'/'.$result['child'].'/'.$result['grandchild'].'/'.$result['grand-grandchild'].'/'.$result['grand-grand-grandchild'];
    }

$key_files = array_combine(array_values($mynewGEO), array_values($mynewGEO));
$tree = explodeTree($key_files, "/", true);
echo makeULLI($tree);

function makeULLI($array) {
    $return = "<ul>\n";

    if (is_array($array) && count($array) > 0) {
        foreach ($array as $k => $v) {
            if($k == "__base_val") continue;
            // determine the __base_val value in orde to use for the link.
            $our_linky = ( is_array($v) ? $v["__base_val"] : $v );

            if (is_array($v) && count($v) > 0) {
                $return .= "\t<li><a href=\"".$our_linky."/\" >" . $k ."</a>". makeULLI($v) . "</li>\n";
            }
            else {
                $return .= "\t<li><a href=\"".$our_linky."/\" >" . $k . "</a></li>\n";
                //to return full path
                //$return .= "\t<li><a href=\" # \" >" . $v . "</a></li>\n";
            }
        }
    } else {}

    $return .= "</ul>";

    return $return;
}

function explodeTree($array, $delimiter = '_', $baseval = false)
{
  if(!is_array($array)) return false;
  $splitRE   = '/' . preg_quote($delimiter, '/') . '/';
  $returnArr = array();
  foreach ($array as $key => $val) {
    // Get parent parts and the current leaf
    $parts  = preg_split($splitRE, $key, -1, PREG_SPLIT_NO_EMPTY);
    $leafPart = array_pop($parts);

    // Build parent structure
    // Might be slow for really deep and large structures
    $parentArr = &$returnArr;
    foreach ($parts as $part) {
      if (!isset($parentArr[$part])) {
        $parentArr[$part] = array();
      } elseif (!is_array($parentArr[$part])) {
        if ($baseval) {
          $parentArr[$part] = array('__base_val' => $parentArr[$part]);
        } else {
          $parentArr[$part] = array();
        }
      }
      $parentArr = &$parentArr[$part];
    }

    // Add the final part to the structure
    if (empty($parentArr[$leafPart])) {
      $parentArr[$leafPart] = $val;
    } elseif ($baseval && is_array($parentArr[$leafPart])) {
      $parentArr[$leafPart]['__base_val'] = $val;
    }
  }
  return $returnArr;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM