Конвертировать массив путей в список UL - PullRequest
9 голосов
/ 05 февраля 2011

У меня есть таблица в базе данных, которая содержит различные пути к страницам моего сайта. Каждый путь указан только один раз. В настоящее время у меня есть очень длинная и запутанная серия запросов и 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/

и я хочу создать следующий вывод:

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

Итак, я подошел очень близко к сценарию, найденному здесь: http://www.daniweb.com/forums/thread285916.html, но я столкнулся с проблемой. Оказывается, что сценарий, который я нашел, создает неправильно отформатированные списки UL. В ПРАВИЛЬНОЙ ситуации подсписок содержится в <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";
}

и он возвращает что-то вроде этого:

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

Кто-нибудь имеет представление о том, где мне нужно добавить дополнительную логику и как я могу это сделать? Другие идеи о лучшем способе сделать это? Кажется, это настолько распространенная проблема, что существует простой / стандартный метод обработки чего-то подобного. Может быть, если бы я мог выяснить, как создать многомерный массив из моих путей, то их можно было бы повторить, чтобы эта работа работала?

<ч />

РЕДАКТИРОВАТЬ: более сложный: - (

Я попробовал ответ Касабланки, и он отлично работал ... за исключением того, что я тогда понял, что теперь у меня есть продолжение, чтобы усложнить ситуацию. Чтобы отобразить «имя» страницы, мне нужно также иметь эту информацию в массиве, таким образом, путь, вероятно, лучше работает как ключ массива и имя в значении. Любые мысли об изменении, как это:

$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"
);

и затем использовать что-то вроде этой строки в функции buildUL:

echo '<a href="'.$prefix.$key.'/">'.$paths[$prefix.$key].'</a>';

Ответы [ 2 ]

13 голосов
/ 05 февраля 2011

Редактировать:

Изменен для обслуживания обновленного вопроса.

Я использую индекс массива __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;

?>
5 голосов
/ 05 февраля 2011

Действительно, многомерность поможет здесь.Вы можете создать его, разбив каждый путь на компоненты и используя их для индексации в массиве.Предполагая, что $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
  }
}

Затем вы можете перебрать этот массив с помощью рекурсивной функции, который вы можете использовать для естественного построения рекурсивного списка 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";
}

Исходныйзвонок будет просто buildUL($array, '').

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...