Преобразование массива путей в список UL

У меня есть таблица в базе данных, которая содержит множество путей к страницам моего сайта. Каждый путь указан только один раз. В настоящее время у меня очень длинная и запутанная серия запросов и PHP, чтобы вытащить все это и переписать данные в неупорядоченный список (для создания меню для моего сайта). Похоже, что существует, вероятно, относительно простой цикл, который будет работать намного эффективнее, но я не могу заставить ничего работать. Я нашел ТОНЫ 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. В ситуации CORRECT под-список содержится внутри <li> родительского элемента. В этом скрипте родительский <li> закрывается, а затем <ul> блок <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> 

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


EDIT: Более сложный 🙁

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

 $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>'; 

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

Изменен, чтобы удовлетворить обновленный вопрос.

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

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