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

发布于 2024-10-03 00:21:18 字数 1215 浏览 1 评论 0原文

我已经尝试解决这个问题有一段时间了,在网上进行研究却一无所获。基本上我有一个代表树结构的 5 列表。您可以将其视为目录列表。我试图从中构建一个无序列表菜单。

这是MYSQL表:


父/子/孙/孙子/孙-孙-孙

France/Aquitaine/Dordogne/Bergerac/Issac

France/Aquitaine/Dordogne/Nontron/Nontron

Cyprus/Paphos/帕福斯区/珊瑚湾/珊瑚湾

丹麦/南丹麦/朗厄兰/鲁德科宾/朗厄兰

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


无序列表应如下所示:

  • 法国
    • 阿基坦
      • 多尔多涅省
        • 贝尔热拉克
          • 艾萨克
        • 农创
          • Nontron
  • 塞浦路斯
    • 帕福斯
      • 帕福斯区
        • 珊瑚湾
          • 珊瑚湾

等...

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

我一直在尝试使用以下功能来尝试得到我想要的东西: 任何

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

画中仙 2024-10-10 00:21:18

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

/** 
 * 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());

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

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.

乖不如嘢 2024-10-10 00:21:18

好吧,我已经设法拼凑出一段似乎有效的代码。

我首先手动将列连接到一维数组中的 /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;
}

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文