从PHP中的5列MYSQL表构建无序列表(树和递归)

时间:2010-11-18 14:06:47

标签: php mysql recursion multidimensional-array html-lists

我一直试图绕过这个问题一段时间,在网上进行的研究没有任何结果。基本上我有一个表示树结构的5列表。您可以将其视为目录列表。我试图建立一个无序的列表菜单。

这是MYSQL表:


父/子/孙子/大-孙子/隆重-隆重-孙子

法国/阿基坦/多尔多涅/贝格拉克/伊萨克

法国/阿基坦/多尔多涅/农特龙/农特龙

塞浦路斯/帕福斯/帕福斯区/珊瑚湾/珊瑚湾

丹麦/南丹麦/兰格兰/ Rudkobing / Langeland

埃及/西奈半岛/红海/沙姆沙伊赫/沙姆沙伊赫


无序列表应如下所示:

      
  • 法国     
            
    • 阿基坦         
                  
      • 多尔多涅             
                        
        • 贝杰拉克                 
                              
          • 艾萨克
          •                 
                        
        •               
        • 农特龙                 
                              
          • 农特龙
          •                 
                        
        •             
                  
      •         
            
    •     
      
  •   
  • 塞浦路斯     
            
    • 帕福斯         
                  
      • 帕福斯区             
                        
        • 珊瑚湾                 
                              
          • 珊瑚湾
          •                 
                        
        •             
                  
      •         
            
    •     
      

等...

基本上生成一个代表所有五个级别的无序列表菜单,正确分组所有内容。

我一直在用以下功能来尝试获得我想要的东西: http://kevin.vanzonneveld.net/techblog/article/convert_anything_to_tree_structures_in_php/

任何人都可以提供有关如何解决此问题的任何进一步信息吗?我试图为每个级别构建循环,但很快发现这是徒劳的,应该使用递归函数。

2 个答案:

答案 0 :(得分:0)

实际上,这里不需要递归。可以通过重复使用插入函数来构建树,并且可以以命令式方式安全地实现该函数。

/** 
 * 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;  
}

一旦你有了这个,如果你的行的格式是array('France','Aquitaine','Dordogne','Bergerac','Issac'),那么在树中插入所有元素都相当容易:

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

可能的优化是按字典顺序对行进行排序,这意味着具有相似前缀的所有行将被一起处理,因此您可以节省一些遍历时间。

答案 1 :(得分:0)

好的,我已经设法凑齐了一段似乎有用的代码。

我首先在一维数组中手动​​将列连接到/ column1 / column2 / column3 / column4 / column5字符串; 然后应用来自相关url的函数将这个简单数组展开为数组树。 最后,我使用makeULLI函数生成一个无序列表,其中包含每个节点的链接。我已经进一步扩展了代码,根据链接的深度添加到URL的自定义路径,以便在SEO友好链接中使用,但我删除了这一点。

我附加了完成所有这些操作的代码并且令人惊讶地小。 代码应该可以在任意数量的级别上运行,我已经在一个包含大约400行(5列)的表上运行它,并且它在0.0168秒内执行。 如果有人能够看到对代码的进一步优化,我将不胜感激。

    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;
}