Need help with Dynamic Directory Tree

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
Swede78
Forum Contributor
Posts: 198
Joined: Wed Mar 12, 2003 12:52 pm
Location: IL

Need help with Dynamic Directory Tree

Post by Swede78 »

I've been attempting to build a dynamic directory tree with limited success. Maybe my logic is wrong, or there's an easier way. I've been messing with this for days, and just can't get it right.

I have a couple fields in my database; FilePath and FileName

Now, let's say I pull all rows from my database, and this is what I get: (FilePath and FileName are put together, and it is sorted by FilePath, FileName).

DRINK/coffee.php
DRINK/milk.php
DRINK/SODA/DARK/cola.php
DRINK/SODA/DARK/rootbeer.php
DRINK/SODA/LIGHT/gingerale.php
FOOD/chicken.php
FOOD/chips.php
FOOD/FRUIT/apples.php
FOOD/FRUIT/oranges.php
FOOD/popcorn.php

My goal is to end up with this:

Code: Select all

<li>DRINK
	<ul>
	<li>coffee.php</li>
	<li>milk.php</li>
	<li>SODA
		<ul>
		<li>DARK
			<ul>
			<li>cola.php</li>
			<li>rootbeer.php</li>
			</ul>
		</li>
		<li>LIGHT
			<ul>
			<li>gingerale.php</li>
			</ul>
		</li>
		</ul>
	</li>
	</ul>
</li>
<li>FOOD
	<ul>
	<li>chicken.php</li>
	<li>chips.php</li>
	<li>FRUIT
		<ul>
		<li>apples.php</li>
		<li>oranges.php</li>
		</ul>
	</li>
	<li>popcorn.php</li>
	</ul>
</li>
The hardest part is closing the list tags at the appropriate places. I just don't know how to create the loop for it. Also, I don't want to limit the directory depth.

I've tried pulling only DISTINCT FilePath's from the database. Going through each FilePath, breaking them apart and storing those parts in an array to get the levels right. Then, I check if the current level's path name matches the last. If not, I create a new list. Unfortunately, I don't know how to let it know when to close the list tag.

I appreciate any help I can get. Thanks.
User avatar
Zoxive
Forum Regular
Posts: 974
Joined: Fri Apr 01, 2005 4:37 pm
Location: Bay City, Michigan

Post by Zoxive »

I would try putting all of those in an array, then explode(); them all up by '/' and use a couple for()'s, and or foreach()'s.
iluxa
Forum Newbie
Posts: 15
Joined: Wed Nov 29, 2006 3:29 am

Post by iluxa »

Redesign your database as follows:

Code: Select all

table directories:
dir_id
parent_dir_id
dir_name

table files:
file_id
dir_id
file_name
This will allow you to easily traverse tree in correct order
by recursively issuing select statements.
User avatar
CoderGoblin
DevNet Resident
Posts: 1425
Joined: Tue Mar 16, 2004 10:03 am
Location: Aachen, Germany

Post by CoderGoblin »

Sitepoint: Storing Hierarchical Data in a Database has a useful article which may be of use, but not specically dealing with your Paths and filenames so you would have to think if it fits into your situation. I prefer using the Modified Preorder Tree Traversal technique as it can avoid recursion (multiple selects when only one is needed can slow the code down considerably).
Swede78
Forum Contributor
Posts: 198
Joined: Wed Mar 12, 2003 12:52 pm
Location: IL

Post by Swede78 »

Thanks everyone! I appreciate the responses. I was thinking I would probably need another table to store parent:child directories as iluxa suggested. Although that method is probably the easiest way, I was able to make a function (posted below) that did what I needed using the full path and file names that already exist, which saves me time from having to update the code to automatically modify the tree structure when a new row is inserted.

Zoxive, your suggestion is what I attempted at first. But, the problem I had was that I couldn't get the loop to know when to close the list tags with the way my data was stored.

What iluxa suggested is called "The Adjacency List Model". Found this, along with the "Modified Preorder Tree Traversal", on the page CoderGoblin suggested. I found that second method mentioned very interesting, but a bit too complicated for my purposes. My method is for maybe up to 200 files total, and is being run locally by only myself. But, I have bookmarked it for future reference.

Thanks again to those that responded. Here's the function I created to make my unordered list tree based on full directory paths + the filename.

Code: Select all

// Dynamic Directory Tree Function
// ============================================
// This function builds a dynamic tree directory in the form of an un-ordered list
function get_DirectoryTree($BasePath='/')
{
	global $MySQLconnection;
	$HTML_output = '<ul>';
	
	
	// this looks for files only
	$query_DirFiles = "SELECT FileName FROM pages WHERE FilePath = '$BasePath'";
	$DirFiles = mysql_query($query_DirFiles, $MySQLconnection) or die(mysql_error());
	$row_DirFiles = mysql_fetch_assoc($DirFiles);
	if( mysql_num_rows($DirFiles) > 0 )
	{
		do {
			$FileName = $row_DirFiles['FileName'];
			// build the output html
			$HTML_output .= '<li>'.$FileName.'</li>';
	
		} while ($row_DirFiles = mysql_fetch_assoc($DirFiles));
	}
	
	// this looks for directories only
	$query_Directories = "SELECT FilePath FROM pages WHERE FilePath LIKE '$BasePath%'";
	$Directories = mysql_query($query_Directories, $MySQLconnection) or die(mysql_error());
	$row_Directories = mysql_fetch_assoc($Directories);
	if( mysql_num_rows($Directories) > 0 )
	{
		$PrevDirName = '';
		
		do {
			$FilePath = $row_Directories['FilePath'];
			
			// get just the next directory (1 level deep)
			// remove the base path from directory name
			$DirName = substr($FilePath, strlen($BasePath));
			// get rid of last slash of the File Path
			if( substr($DirName, -1, 1) == '/' ) { $DirName = substr($DirName, 0, -1); }
			// put directories in an array, and use only the first level
			$DirParts = explode('/', $DirName);
			$DirName = $DirParts[0];
			
			// if a sub directory exists and it's not the same as the previous one,
			// create a folder, and check for subdirectories / files
			if( $DirName != '' && $DirName != $PrevDirName )
			{
				$HTML_output .= '<li>'.strtoupper($DirName);
				
				// check for more levels
				// use the basepath + next level directory + a slash
				$SubDirectory = get_DirectoryTree($BasePath.$DirName.'/');
				if( $SubDirectory != '' ) { $HTML_output .= $SubDirectory; }
				
				$HTML_output .= '</li>';
			}
			
			$PrevDirName = $DirName;
		
		} while ($row_Directories = mysql_fetch_assoc($Directories));
	}
	

	$HTML_output .= '</ul>';
	return $HTML_output;
}

After getting my function to spit out the unordered lists, I used a style sheet and javascript provided by http://www.dynamicdrive.com/dynamicindex1/navigate1.htm to create the look/layout and the expand/collapse function. (It requires that it be applied to an un-ordered list format.)

Thanks, Swede
Swede78
Forum Contributor
Posts: 198
Joined: Wed Mar 12, 2003 12:52 pm
Location: IL

Post by Swede78 »

something strange is going on with this site. My post above does not appear unless this post is here. I came here to check the topic, and my previous post was missing. Not on another page or anything. I was going to re-post my message (a condensed version), and as soon as I did, my previous post appeared. So, I deleted my condensed version, and my previous post dissappeared again. Wtf?
Post Reply