将路径数组转换为UL列表

tec*_*tre 9 php tree recursion path

我在数据库中有一个表,其中包含我网站页面的各种路径.每个路径仅列出一次.我目前有一个非常漫长而复杂的查询系列和PHP来提取所有这些并将数据重写为无序列表(为我的网站创建菜单).似乎可能有一个相对简单的循环方法可以更有效地工作,但我似乎无法得到任何工作.我发现TONS的PHP脚本从文件树创建UL列表,但是它们都不起作用或者无法处理我的查询结果的固有非递归性质(有些需要我的路径的多维数组,没问题,除非我创造那些麻烦.我确实找到了一个非常接近的脚本,但它<ul>通过在子<li>节之外放置子列表来错误地格式化部分(我将在下面解释)

这是一个示例:

DB在结果数组中返回以下内容:

about/contact/
about/contact/form/
about/history/
about/staff/
about/staff/bobjones/
about/staff/sallymae/
products/
products/gifts/
products/widgets/
Run Code Online (Sandbox Code Playgroud)

我想创建以下输出:

<ul>
  <li>about/
  <ul>
    <li>about/contact/
    <ul>
      <li>about/contact/form/</li>
    </ul>
    </li>
    <li>about/history/</li>
    <li>about/staff/
    <ul>
      <li>about/staff/bobjones/</li>
      <li>about/staff/sallymae/</li>
    </ul>
    </li>
  </ul>
  </li>
  <li>products/
  <ul>
    <li>products/gifts/</li>
    <li>products/widgets/</li>
  </ul>
  </li>
</ul>
Run Code Online (Sandbox Code Playgroud)

所以我非常接近这里找到的脚本:http: //www.daniweb.com/forums/thread285916.html 但是我遇到了一个问题.事实证明,我发现的脚本会创建格式不正确的UL列表.在CORRECT情况下,子列表包含在<li>父元素中.在此脚本中,父<li>关闭,然后<ul>插入一个块.整个脚本实际上是相当优雅的,它保持水平等等,但我无法绕过它足以弄清楚如何解决它.我在这里有一个函数:

function generateMainMenu()
{
  global $db;

  $MenuListOutput = '';
  $PathsArray = array();

  $sql = "SELECT PageUrlName FROM `table`";
  $result = mysql_query($sql, $db) or die('MySQL error: ' . mysql_error());
  while ($PageDataArray = mysql_fetch_array($result))
  {
    $PathsArray[] = rtrim($PageDataArray['PageUrlName'],"/"); //this function does not like paths to end in a slash, so remove trailing slash before saving to array
  }

  sort($PathsArray);// These need to be sorted.
  $MenuListOutput .= '<ul id="nav">'."\n";//get things started off right
  $directories=array ();
  $topmark=0;
  $submenu=0;
  foreach ($PathsArray as $value) {
    // break up each path into it's constituent directories
    $limb=explode("/",$value);
    for($i=0;$i<count($limb);$i++) {
      if ($i+1==count($limb)){
        // It's the 'Leaf' of the tree, so it needs a link
        if ($topmark>$i){
          // the previous path had more directories, therefore more Unordered Lists.
          $MenuListOutput .= str_repeat("</ul>",$topmark-$i); // Close off the Unordered Lists
          $MenuListOutput .= "\n";// For neatness
        }
        $MenuListOutput .= '<li><a href="/'.$value.'">'.$limb[$i]."</a></li>\n";// Print the Leaf link
        $topmark=$i;// Establish the number of directories in this path
      }else{
        // It's a directory
        if($directories[$i]!=$limb[$i]){
          // If the directory is the same as the previous path we are not interested.
          if ($topmark>$i){// the previous path had more directories, therefore more Unordered Lists.
            $MenuListOutput .= str_repeat("</ul>",$topmark-$i);// Close off the Unordered Lists
            $MenuListOutput .= "\n";// For neatness
          }

          // (next line replaced to avoid duplicate listing of each parent)
          //$MenuListOutput .= "<li>".$limb[$i]."</li>\n<ul>\n";
          $MenuListOutput .= "<ul>\n";
          $submenu++;// Increment the dropdown.
          $directories[$i]=$limb[$i];// Mark it so that if the next path's directory in a similar position is the same, it won't be processed.
        }
      }
    }
  }
  $MenuListOutput .= str_repeat("</ul>",$topmark+1);// Close off the Unordered Lists

  return $MenuListOutput."\n\n\n";
}
Run Code Online (Sandbox Code Playgroud)

它返回如下内容:

<ul id="nav">
<li><a href="/about">about</a></li>
<ul>
<li><a href="/about/history">history</a></li>
<li><a href="/about/job-opportunities">job-opportunities</a></li>
<li><a href="/about/mission">mission</a></li>
<li><a href="/about/privacy-policy">privacy-policy</a></li>
</ul>
<li><a href="/giftcards">giftcards</a></li>
<li><a href="/locations">locations</a></li>
<ul>
<li><a href="/locations/main-office">main-office</a></li>
<li><a href="/locations/branch-office">branch-office</a></li>
</ul>
<li><a href="/packages">packages</a></li>
</ul>
Run Code Online (Sandbox Code Playgroud)

任何人都知道我需要在一些额外的逻辑中添加哪些内容以及如何实现这一目标?有关更好方法的其他想法吗?看起来这是一个常见的问题,有一个简单/标准的方法来处理这样的事情.也许如果我能弄清楚如何从我的路径创建一个多维数组,那么可以迭代这些以使其工作?


编辑:更复杂:-(

我尝试了卡萨布兰卡的反应,它完美地工作......除了我后来才意识到现在我有一个后续工作让事情变得更加困难.为了显示页面的"名称",我还需要在数组中包含该信息,因此路径可能更适合作为数组键和值中的名称.有关改变的任何想法:

$paths = array(
    "about/contact/ " => "Contact Us", 
    "about/contact/form/ " => "Contact Form",
    "about/history/ " => "Our History",
    "about/staff/ " => "Our Staff",
    "about/staff/bobjones/ " => "Bob",
    "about/staff/sallymae/ " => "Sally",
    "products/ " => "All Products",
    "products/gifts/ " => "Gift Ideas!",
    "products/widgets/ " => "Widgets"
);
Run Code Online (Sandbox Code Playgroud)

然后在buildUL函数中使用类似这样的东西:

echo '<a href="'.$prefix.$key.'/">'.$paths[$prefix.$key].'</a>';
Run Code Online (Sandbox Code Playgroud)

Lei*_*igh 13

编辑:

更改为迎合更新的问题.

我正在使用数组索引__title来保存页面标题.只要你的树中没有一个目录,就__title可以了.您可以随意将此哨兵值更改为您想要的任何内容.

我也更改了它,因此列表构建函数返回一个字符串,以便您可以存储该值以便稍后在页面中使用.(你当然可以echo build_list(build_tree($paths))直接输出列表.

<?php

$paths = array(
    'about/contact/' => 'Contact Us', 
    'about/contact/form/' => 'Contact Form',
    'about/history/' => 'Our History',
    'about/staff/' => 'Our Staff',
    'about/staff/bobjones/' => 'Bob',
    'about/staff/sallymae/' => 'Sally',
    'products/' => 'All Products',
    'products/gifts/' => 'Gift Ideas!',
    'products/widgets/' => 'Widgets'
);

function build_tree($path_list) {
    $path_tree = array();
    foreach ($path_list as $path => $title) {
        $list = explode('/', trim($path, '/'));
        $last_dir = &$path_tree;
        foreach ($list as $dir) {
            $last_dir =& $last_dir[$dir];
        }
        $last_dir['__title'] = $title;
    }
    return $path_tree;
}

function build_list($tree, $prefix = '') {
    $ul = '';
    foreach ($tree as $key => $value) {
        $li = '';
        if (is_array($value)) {
            if (array_key_exists('__title', $value)) {
                $li .= "$prefix$key/ <a href=\"/$prefix$key/\">${value['__title']}</a>";
            } else {
                $li .= "$prefix$key/";
            }
            $li .= build_list($value, "$prefix$key/");
            $ul .= strlen($li) ? "<li>$li</li>" : '';
        }
    }
    return strlen($ul) ? "<ul>$ul</ul>" : '';
}

$tree = build_tree($paths);
$list = build_list($tree);
echo $list;

?>
Run Code Online (Sandbox Code Playgroud)


cas*_*nca 5

事实上,多维度在这方面会有所帮助.您可以通过将每个路径拆分为组件并使用它们索引到数组中来构建一个.假设$paths是您的初始数组,下面的代码将构建一个多维数组,$array其中的键对应于路径组件:

$array = array();
foreach ($paths as $path) {
  $path = trim($path, '/');
  $list = explode('/', $path);
  $n = count($list);

  $arrayRef = &$array; // start from the root
  for ($i = 0; $i < $n; $i++) {
    $key = $list[$i];
    $arrayRef = &$arrayRef[$key]; // index into the next level
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用递归函数迭代此数组,您可以使用该函数自然地构建递归UL列表,如示例所示.在每个递归调用中,$array是当前正在处理的整个数组的子数组,并且$prefix是从根到当前子数组的路径:

function buildUL($array, $prefix) {
  echo "\n<ul>\n";
  foreach ($array as $key => $value) {
    echo "<li>";
    echo "$prefix$key/";
    // if the value is another array, recursively build the list
    if (is_array($value))
      buildUL($value, "$prefix$key/");
    echo "</li>\n";
  }
  echo "</ul>\n";
}
Run Code Online (Sandbox Code Playgroud)

最初的电话会是buildUL($array, '').