Welcome to WebmasterWorld Guest from 35.153.73.72

Forum Moderators: coopster & jatar k

Message Too Old, No Replies

Convert array of file paths into UL list

     
5:28 am on Jan 26, 2011 (gmt 0)

Junior Member

10+ Year Member

joined:May 12, 2007
posts: 91
votes: 0


I have a table in a database that contains a variety of file paths to pages of my website. Each path is listed only one time. I currently have a very long and convoluded series of queries and php to pull all these and rewrite the data into an unordered list (to create a menu for my website). It seems that there is probably a relatively simple looping approach that would work MUCH more efficiently, but i cannot even think of where to begin. Anyone have any ideas to help me get started? Here is a sample:

DB returns the following in results array:
about/contact/
about/contact/form/
about/history/
about/staff/
about/staff/bobjones/
about/staff/sallymae/
products/
products/gifts/
products/widgets/

and i want to create the following output:
<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>

Any help you can give to mapping a file directory directly to a UL list will be very helpful. Thanks!
11:47 am on Jan 26, 2011 (gmt 0)

Senior Member from MY 

WebmasterWorld Senior Member vincevincevince is a WebmasterWorld Top Contributor of All Time 10+ Year Member

joined:Apr 1, 2003
posts:4847
votes: 0


I'm not sure I can think of a clever way to do it, however - I would suggest that usually when we've stored this kind of data it has been in a hierarchical table, that each row knows its parent, and we read it in loops starting with those which have no parent.
5:58 pm on Jan 26, 2011 (gmt 0)

Senior Member

WebmasterWorld Senior Member rocknbil is a WebmasterWorld Top Contributor of All Time 10+ Year Member

joined:Nov 28, 2004
posts:7999
votes: 0


Don't have time to look up the code (I know I have this somewhere, but might be in perl.) What I figured out is

- create a subroutine (function in PHP) to read a directory. Initially you point this at the "parent directory." You want this to be a function because you will call it recursively. Structure this function so that it a) sets the return string null on entry, so it doesn't concatenate previous data, and b) returns a string chunk that is a complete <ul>(list items)</ul>

- As you begin stepping through the directory, you do a test against -f and -d (file or directory.) if file, add to list as an li. Don't close the li yet until you read the next object.

- if the next object is a directory, call the function again to get that directory's contents - this of course will occur within the function itself. (For development, it's a Really Good Idea to create a global variable $count and keep track of iterations in case you corner yourself in an infinite loop. When $maxcount is exceeded, exit immediately. Trust me on this one, it's easy to do when calling a function from within itself. :-) )

- When the depth of directories is reached, it will close the previous <li> and move to the next object. When the directory is fully traversed, close with a <ul> and return.

Or - the smart thing to do - would be to look for an open source class that's already been built for this purpose. It's great to explore this as an exercise if you have the time, but I have no doubt there's probably several hundred sites out there that offer a class or script that does this already.
5:14 am on Jan 28, 2011 (gmt 0)

Junior Member

10+ Year Member

joined:May 12, 2007
posts: 91
votes: 0


I have found some code that seems to be just about what i need...i am now working to get it customized to work for me and i will post that when it is working, but for now the original code is here:
[daniweb.com...]
7:23 am on Jan 30, 2011 (gmt 0)

Junior Member

10+ Year Member

joined:May 12, 2007
posts: 91
votes: 0


So...i have run into a problem. It turns out that the script that i found creates improperly formatted UL lists. In a CORRECT situation, a sub-list is contained within the <li> of the parent element. In this scripting, the parent <li> is closed and then a <ul> block is inserted. The overall script is actually fairly elegant in the way that it keeps up with the levels and such, but i cannot wrap my head around it enough to figure out how to fix it. I have the whole thing in a function here:

function generateMainMenu()
{
//modified from post found here: [daniweb.com...]
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";
}



and it returns something like this:
<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>


Anyone have an idea of where i need to add in some additional logic and how i can accomplish this? THANKS!
3:35 am on Feb 2, 2011 (gmt 0)

Junior Member

10+ Year Member

joined:May 12, 2007
posts: 91
votes: 0


anyone?
10:01 am on Feb 2, 2011 (gmt 0)

Junior Member

10+ Year Member

joined:May 9, 2005
posts: 46
votes: 0


You've shown us the result array from your DB for the categories but not how the tables are set up. Can you post an idea of how the tables are structred?

cheers,

Wittner
6:24 pm on Feb 2, 2011 (gmt 0)

Senior Member

WebmasterWorld Senior Member rocknbil is a WebmasterWorld Top Contributor of All Time 10+ Year Member

joined:Nov 28, 2004
posts:7999
votes: 0


It's not the DB, it's the way the script is reading directories.

No time to mod this script ATM, but looking at it I'd say - keep looking, find another one. :-)
3:30 pm on Feb 3, 2011 (gmt 0)

Junior Member

10+ Year Member

joined:May 12, 2007
posts: 91
votes: 0


@wittner: The database is not the issue on this one...the query i am using is returning complete path from root for each row like this:
/about/
/about/history/
/about/job-opportunities/
/about/mission/
etc...

@rocknbil: your original post is very solid logic on how to write this from scratch...but i feel like with the current code i found i am SO CLOSE to it working that i should just continue with it. I simply cannot figure out where to move the <ul> open/close section to get it in the proper place. Will this really require major logic or is there not a simple approach?

@everyone: Is there not a straight-forward approach to processing directory trees in PHP...it seems like this would be one of those "basic core functions" that everyone needs and uses all the time.
8:15 pm on Feb 5, 2011 (gmt 0)

Junior Member

10+ Year Member

joined:May 12, 2007
posts: 91
votes: 0


SOLUTION:

<?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 .= sprintf('%s%s/ <a href="/%s%s/">%s</a>', $prefix, $key, $prefix, $key, $value['__title']);
} else {
$li .= "$prefix$key/";
}
$li .= build_list($value, "$prefix$key/");
$ul .= strlen($li) ? sprintf('<li>%s</li>', $li) : '';
}
}
return strlen($ul) ? sprintf('<ul>%s</ul>', $ul) : '';
}

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

?>