Properly working Database driven navigation - Part II

Comments and improovements more than welcome!

Page 1 of 1

1 Replies - 3761 Views - Last Post: 15 August 2009 - 07:14 AM Rate Topic: -----

#1 ComboAlex   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 34
  • Joined: 31-July 09

Properly working Database driven navigation - Part II

Post icon  Posted 09 August 2009 - 06:22 PM

Well, almost no one had mercy on me in my previous posts where I was asking for help (here), so I had to deal with it by myself. Shame on you, no one helped. :angry:

Only joking! :D It wasn't really a walk in the park for me at this stage, but I must say that I learned so much new stuff while I was figuring out the solution. I guess that's the idea, right? The right way to become a good programmer. :) Work, work work... :) Had a hard time, I must say, cause I was trying too hard, not taking enough breaks... That was probably a mistake. But I knew I was at the edge of the solution, so I was thinking about it all 3 whole days. Even when I went to bed in early morning hours (like I will today :)) I was still going through the problem in my mind while already in bed.

As I said I learned some nice and fancy solutions: I used a recursion, I learned that variables in CF functions can make a big mess if you don't specify them as local. That means adding a "var" before it when initializing... I thought it is normal to expect that if you set a new variable within the function, that it becomes only local... apparently I was wrong, wasn't I? If someone thinks differently I am keen to learn more. I also made a few interesting and usable functions (was good practice), I used the stack mechanism (PUSH, POP) and I even applied to CF my knowledge about arrays of structs from C language. That stuff is really useful! Now I am really eager to see if I can apply to CF programming the same knowledge I have about linked lists (C language again). :)

So, here it is, my properly working code. I was trying to make it as optimal as possible, but I still had to make the use of queries twice while displaying the main navigation and I couldn't see myself doing the job without at least one recursive call, despite someone very expert told me that it could be realised completely without recursions. I couldn't figure that part out, so I will be more than happy to read about your opinions. I will be also glad to see if someone can make my code more optimal (less code, faster performance, less hardcoding, more useful abstraction for different outlook...).

So, feel free to check out the code and seek for mistakes. Please, be my guest!

Do you think I could make a .cfc file and use the functions as components? Would mean that all the functions would have to be public?

Looking forward to hear your opinions and see your improvements, or hearing about your experiences with these kind of navigation.

Bear in mind I was not trying to make is nice-looking at this stage. My main goal was to make it work as optimally as possible.

Here is the code and the database definition.

Posted Image

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Navigation</title>
</head>

<body>

<!-------------------------------------------------------------------->
<!----------- RECURSIVE, DATABASE DRIVEN NAVIGATION MENU ------------->
<!-------------------------------------------------------------------->
<!-------- Author : Aleksander Rebula, Slovenia (August 2009) -------->
<!-------------------------------------------------------------------->


<!------------------------ functions only----------------------------->
<!-------------------------------------------------------------------->

<!--- The function accepts a given aray, reverses it and returns the reversed array - WORKS OK --->
<!--- Why the heck CF doesn't have a built-in function that reverses a given array? :) --->
<cffunction name="reverseArray" access="public" returntype="array">
	<CFARGUMENT name="inputArray" type="array" required="yes" default=0 >	
	<CFSCRIPT>			
		For (i = 1; i LTE (ArrayLen(inputArray)/2); i++)
		{	
			temp1 = i;
			temp2 = ArrayLen(inputArray) + 1 - i; 					
			newArray[i] = inputArray[temp2];
			newArray[temp2] = inputArray[temp1];											
		}
		if (ArrayLen(inputArray) mod 2 != 0)
			newArray[(ArrayLen(inputArray)+1)/2] = inputArray[(ArrayLen(inputArray)+1)/2];		
		return newArray;		
	</CFSCRIPT>   
</cffunction>


<!--- The function makes an array of navigation "bread crumbs" (parentid numeric values) and returns it in
a correct order --->
<cffunction name="MakeBreadCrumb" access="public" returntype="array">
	<CFARGUMENT name="id" type="numeric" required="yes" default=0 >
	<CFARGUMENT name="cnt" type="numeric" required="yes" default=0 >			
	
	<!--- we make a new array which will hold all the parents (parentid values), but we declare it only the
	first time in the recursive call --->	
	<cfif arguments.cnt IS 0>		
		<cfset breadCrumbArray=ArrayNew(1)>
	</cfif>		
	
	<!--- selecting the current parentid --->	
	<cfquery name="qryItem" datasource="#databaseSource#">
		SELECT parentid
		FROM #sourceTable#
		WHERE id = #arguments.id#
	</cfquery>
	
	<!--- saving the current parentid to the array --->
	<cfset breadCrumbArray[#arguments.cnt#]=arguments.id>
	
	<cfif qryItem.parentid IS NOT 0> <!--- if we reached the root level of the navigation --->
		<!--- HERE WE MAKE THE RECURSIVE CALL of "MakeBreadCrumb" function - the input argument
		becomes the current <parentid> from the <qryItem> query --->
		<cfset MakeBreadCrumb(id = qryItem.parentid, cnt = cnt + 1) >
	<cfelse> <!---  if we still haven't reached the root level --->
		<!--- we reverse the breadCrumb array so we can "follow the bread crumbs" from start to the end
		in the correct order --->
		<!--- here is where we use the above written "reverseArray" function --->		
		<cfset reversedBreadCrumbArray = reverseArray(inputArray = breadCrumbArray)>		
	</cfif>
	
	<cfreturn reversedBreadCrumbArray> 
	
</cffunction>


<!--- the function returns a boolean value (true or false) depending if the input argument is in
the specified array --->
<cffunction name="inArray" access="public" returntype="boolean">
	<cfargument name="element" type="numeric">
	<cfargument name="inputArray" type="array">
	
	<cfset arrayLength = ArrayLen(arguments.inputArray)>			
	
	<cfloop from="1" to="#arrayLength#" index="i">		
		<cfif arguments.inputArray[i] EQ arguments.element>						
			<cfreturn true>
			<!--- if this happens only once it's enough to say that the element exists in the inputArray,
			so we exit the function here  --->		  
		</cfif>	 	
	</cfloop>	
	
	<!--- if we get until here it means that the <cfif> statement above was never true and it's content was every time
	skipped--->
	<cfreturn false> 
</cffunction>


<!--- PUSH AND POP HERE --->
<!------------------------->

<!--- the function adds an element (struct) to the specified stack and returns the stack with the new element on it--->
<cffunction name="push" access="public" returntype="void">
	<cfargument name="inputStruct" type="struct" required="yes">
	<cfset ArrayAppend(stack,arguments.inputStruct)>  
</cffunction>


<!--- the function retrieves (pops) and element (a struct) from the stack and returns the stack that lacks of the taken element --->
<!--- it's just basic "stack mechanism" --->
<cffunction name="pop" access="public" returntype="struct">		   
	<cfset var poppedElement = 0>
	<!--- POP from stack - take from stack and delete --->		
	<cfset arrayLength = ArrayLen(stack)>
	<cfset poppedElement = stack[#arrayLength#]> <!--- take from stack --->
	<cfset ArrayDeleteAt(stack,arraylength)><!--- delete from stack --->	
	<cfreturn poppedElement>	
</cffunction>


<!--- the function finds and displays the correct branch in the navigation --->
<cffunction name="findAndDisplayChildren" access="public" returntype="void"> <!--- doesn't return anything, only displays  --->
	<cfargument name="inputElement" type="struct" required="yes">	
	<cfargument name="inputCnt" type="numeric" required="yes">
	
	<!--- we need to be careful not to let the variables in the functions global...
	... can make a mess otherwise, especially with variables with same names :) --->
	<cfset var poppedElement = 0> 
	
	<!--- we query for the the shildren of the choosen parent (inputElement) --->
	<cfquery name="qryChildren" datasource="#databaseSource#">
		SELECT *
		FROM #sourceTable#
		WHERE parentid = #inputElement.id#
	   	ORDER BY navorder DESC		
	</cfquery>
	
	<!--- we loop through the query results and each time we make a new struture --->
	<!--- then we assign values to the structure --->
	<cfloop query="qryChildren">
		<cfset structItem = StructNew()>
		<cfset structItem.id = qryChildren.id>
		<cfset structItem.navdescription = qryChildren.navdescription>
		<cfset structItem.link = qryChildren.link>
		<cfset structItem.navorder = qryChildren.navorder>
		<cfset structItem.parentid = qryChildren.parentid>
		<cfset structItem.level = #arguments.inputCnt#>
		
		<cfset push(inputStruct = structItem)> <!--- PUSH --->
	 </cfloop> 
	 
	<!--- we loop trough the items in the same level (stack[ArrayLen(stack)].level EQ arguments.inputCnt) --->	
	<cfloop condition="stack[ArrayLen(stack)].level EQ arguments.inputCnt">	
	
		<!--- <cfdump var="#stack#"> --->
		<cfset poppedElement = pop()> <!--- POP  --->		
		
		<!--- DISPLAY OTHER THAN SOOT ITEMS - MAKE CSS ADJUSTMENTS HERE --->
		<cfoutput>
			<div style="padding-left:#poppedElement.level*20#px">			
				<a href="alexnavigation.cfm?#poppedElement.link#">#poppedElement.navdescription#</a> 
			</div>
		</cfoutput>
			
		<!--- THE ONLY, BUT MOST IMPORTANT RECURSIVE CALL HERE --->
		<!--- we pass trough only the items that are "defined" by the "list" of parents in "bread crumb" array --->
		<cfif (inArray(element = poppedElement.id, inputArray = breadCrumbArray) IS TRUE)> 
			<cfset findAndDisplayChildren(inputElement = poppedElement, inputCnt = arguments.inputCnt+1)>   
		</cfif> 
		
	</cfloop>
</cffunction>


<!--- the function displays the root menu and the proper branch of submenu (children) --->
<cffunction name="displayMenu" access="public" returntype="void"> <!--- the function won't be returning anything, only displaying --->
	<cfargument name="inputItem" type="numeric" required="yes">
	<cfargument name="inputStack" type="array" required="yes">	

	<cfset var poppedElement = 0> <!--- defining a local variable --->
	
	<!---  we define a new structure that will hold the data of one navigation item --->
	<cfset struct = StructNew()>
	
	<!--- we query only the root items from the table --->
	<cfquery name="qryRootItems" datasource="#databaseSource#">
		SELECT *
		FROM #sourceTable# <!--- defined in the main program (main variables) --->
		WHERE parentid = 0
	   	ORDER BY navorder DESC
	</cfquery>
	
	<!--- we loop trough the query results and each time we make a new struture --->
	<!--- then we assign values to the structure --->
	<cfloop query="qryRootItems">
		<cfset struct = StructNew()> <!--- make a new wtruct --->
		<cfset struct.id = qryRootItems.id>
		<cfset struct.navdescription = qryRootItems.navdescription>
		<cfset struct.link = qryRootItems.link>
		<cfset struct.navorder = qryRootItems.navorder>
		<cfset struct.parentid = qryRootItems.parentid>
		<cfset struct.level = 0>					
		<cfset push(inputStruct = struct)> <!--- PUSH - adding on stack--->
		<!--- we use the "stack mechanism" in order be able to display the navigation items correctly  --->
		
	</cfloop>
	
   <!--- we will push and pop until the stack ios empty  --->
   <!--- once the stack is empty, the whole navigation is displayed on the screen --->
   <cfloop condition="NOT ArrayIsEmpty(stack)"> 
		
		<cfset poppedElement = pop()> <!--- POP - taking from the stack --->
		
		<!--- DISPLAY ROOT ITEMS - MAKE CSS ADJUSTMENTS HERE --->
		<!--- we only display root items here --->
		<cfif poppedElement.level IS 0>
			<cfoutput>
				<div><a href="#urlFileName#?#poppedElement.link#">#poppedElement.navdescription#</a></div><!--- display the item --->
			</cfoutput>
		</cfif>
		
		<!--- we only display items with the proper parent --->
		<!--- prober parents are stored in the "breadCrumbArray" --->
		<cfif (inArray(element = poppedElement.id, inputArray = breadCrumbArray) IS TRUE)>	   
			<cfset findAndDisplayChildren(inputElement = poppedElement, inputCnt = 1)> <!--- this is a recursive function --->
		</cfif>
		
   </cfloop>
	
</cffunction>


<!-------------------------------------------------------------------->
<!--------------------------- "main program" ------------------------->
<!-------------------------------------------------------------------->


<!--- main variables --->
<!---------------------->

<cfset databaseSource = "test"> <!--- defining the datasource (database name) --->
<cfset sourceTable = "testnavigation"> <!--- defining the table we will be getting the data from --->
<cfset urlFileName="alexnavigation.cfm"> <!--- defining the file we will be using in the navigation links --->
<cfset stack = ArrayNew(1) > <!--- initializing the stack --->

<!--- let's call the function that makes the breadCrumb --->
<cfset breadCrumbcArray = MakeBreadCrumb(id=URL.item, cnt=1)>

<!--- let's display the navigation menu --->
<cfset displayMenu(inputItem = URL.item,inputStack = variables.stack)>

</body>
</html>
 



Is This A Good Question/Topic? 0
  • +

Replies To: Properly working Database driven navigation - Part II

#2 ComboAlex   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 34
  • Joined: 31-July 09

Re: Properly working Database driven navigation - Part II

Posted 15 August 2009 - 07:14 AM

Hey there.

I made some changes... included components. I am trying to make the code as flexible as possible.

Is anyone willing to give me his review of my code? Opinions would be mostly appreciated.

I am also talking about it here: http://www.coldfusio...otor-is-running

What I am figuring out now is how to properly set some global variables (in a general config file) like the "database source" and "source database table" in order not to pass those values trough function arguments every time, for every function. That can be a bit annoying if you have some same arguments in every function, like in my case.

Here's my code...


The index.cfm file:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Title</title>
</head>

<body>

<!--- main variables --->
<!---------------------->
<cfinclude template="includes/inc_config.cfm">


<!--- full-time services --->
<!-------------------------->
<cfinclude template="includes/inc_navigation.cfm"> <!--- displaying navigation --->


<!--- chosen temp services --->
<!---------------------------->



</body>
</html>




inc_config.cfm
<!--- main variables --->
<cfset databaseSource = "test"> <!--- defining the datasource (database name) --->
<cfset databaseTable = "testnavigation"> <!--- defining the table we will be getting the data from --->
<cfset urlFileName="index.cfm"> <!--- defining the file we will be using in the navigation links --->



inc_navigation.cfm
<!--- regarding of the clicked item in the menu, this function makes a bread crumb array
which we need as input argument in the invokeargument for the  method="displayMenu" --->
<cfinvoke 
 component="cfdocs.GlobeKrasCMS.cfcomponents.mainNavigation"
 method="MakeBreadCrumb"
 returnvariable="MakeBreadCrumbRet"> 
	<cfinvokeargument name="id" value="#URL.item#"/>
	<cfinvokeargument name="cnt" value="1"/>
	<cfinvokeargument name="dataSource" value="#databaseSource#"/>
	<cfinvokeargument name="dataTable" value="#databaseTable#"/>
</cfinvoke>

<!--- displaying the main navigation using: URL.item, databaseSource, databaseTable, MakeBreadCrumbRet --->
<cfinvoke 
 component="cfdocs.GlobeKrasCMS.cfcomponents.mainNavigation"
 method="displayMenu">
	<cfinvokeargument name="inputItem" value="#URL.item#"/>
	<cfinvokeargument name="dataSource" value="#databaseSource#"/>
	<cfinvokeargument name="dataTable" value="#databaseTable#"/>
	<cfinvokeargument name="breadCrumbArray" value="#MakeBreadCrumbRet#"/>
</cfinvoke>



The component:
<cfcomponent displayName="mainNavigation">

<!-------------------------------------------------------------------->
<!----------- RECURSIVE, DATABASE DRIVEN NAVIGATION MENU ------------->
<!-------------------------------------------------------------------->
<!-------- Author : Aleksander Rebula, Slovenia (August 2009) -------->
<!-------------------------------------------------------------------->


<!---  defining variables --->
<cfset stack = ArrayNew(1) > <!--- initializing the stack --->


<!------------------------ functions only----------------------------->
<!-------------------------------------------------------------------->


<!--- The function accepts a given aray, reverses it and returns the reversed array - WORKS OK --->
<!--- Why the heck CF doesn't have a built-in function that reverses a given array? :) --->
<cffunction name="reverseArray" access="public" returntype="array">
	<CFARGUMENT name="inputArray" type="array" required="yes" default=0 >	
	<CFSCRIPT>			
		For (i = 1; i LTE (ArrayLen(inputArray)/2); i++)
		{	
			temp1 = i;
			temp2 = ArrayLen(inputArray) + 1 - i; 					
			newArray[i] = inputArray[temp2];
			newArray[temp2] = inputArray[temp1];											
		}
		if (ArrayLen(inputArray) mod 2 != 0)
			newArray[(ArrayLen(inputArray)+1)/2] = inputArray[(ArrayLen(inputArray)+1)/2];		
		return newArray;		
	</CFSCRIPT>   
</cffunction>


<!--- The function makes an array of navigation "bread crumbs" (parentid numeric values) and returns it in
a correct order --->
<cffunction name="MakeBreadCrumb" access="public" returntype="array">
	<CFARGUMENT name="id" type="numeric" required="yes" default=0 >
	<CFARGUMENT name="cnt" type="numeric" required="yes" default=0 >
	<CFARGUMENT name="dataSource" type="string" required="yes">
	<CFARGUMENT name="dataTable" type="string" required="yes">			
			   
	
	<!--- we make a new array which will hold all the parents (parentid values), but we declare it only the
	first time in the recursive call --->	
	<cfif arguments.cnt IS 0>		
		<cfset breadCrumbArray=ArrayNew(1)>
	</cfif>		
	
	<!--- selecting the current parentid --->	
	<cfquery name="qryItem" datasource="#arguments.dataSource#">
		SELECT parentid
		FROM #arguments.dataTable#
		WHERE id = #arguments.id#
	</cfquery>
	
	<!--- saving the current parentid to the array --->
	<cfset breadCrumbArray[#arguments.cnt#]=arguments.id>
	
	<cfif qryItem.parentid IS NOT 0> <!--- if we reached the root level of the navigation --->
		<!--- HERE WE MAKE THE RECURSIVE CALL of "MakeBreadCrumb" function - the input argument
		becomes the current <parentid> from the <qryItem> query --->
		<cfset MakeBreadCrumb(id = qryItem.parentid, cnt = arguments.cnt + 1, dataSource = arguments.datasource, dataTable = arguments.dataTable) >
	<cfelse> <!---  if we still haven't reached the root level --->
		<!--- we reverse the breadCrumb array so we can "follow the bread crumbs" from start to the end
		in the correct order --->
		<!--- here is where we use the above written "reverseArray" function --->		
		<cfset reversedBreadCrumbArray = reverseArray(inputArray = breadCrumbArray)>		
	</cfif>
	
	<cfreturn reversedBreadCrumbArray> 
	
</cffunction>


<!--- the function returns a boolean value (true or false) depending if the input argument is in
the specified array --->
<cffunction name="inArray" access="public" returntype="boolean">
	<cfargument name="element" type="numeric">
	<cfargument name="inputArray" type="array">
	
	<cfset arrayLength = ArrayLen(arguments.inputArray)>			
	
	<cfloop from="1" to="#arrayLength#" index="i">		
		<cfif arguments.inputArray[i] EQ arguments.element>						
			<cfreturn true>
			<!--- if this happens only once it's enough to say that the element exists in the inputArray,
			so we exit the function here  --->		  
		</cfif>	 	
	</cfloop>	
	
	<!--- if we get until here it means that the <cfif> statement above was never true and it's content was every time
	skipped--->
	<cfreturn false> 
</cffunction>


<!--- PUSH AND POP HERE --->
<!------------------------->

<!--- the function adds an element (struct) to the specified stack and returns the stack with the new element on it--->
<cffunction name="push" access="public" returntype="void">
	<cfargument name="inputStruct" type="struct" required="yes">
	<cfset ArrayAppend(stack,arguments.inputStruct)>  
</cffunction>


<!--- the function retrieves (pops) and element (a struct) from the stack and returns the stack that lacks of the taken element --->
<!--- it's just basic "stack mechanism" --->
<cffunction name="pop" access="public" returntype="struct">		   
	<cfset var poppedElement = 0>
	<!--- POP from stack - take from stack and delete --->		
	<cfset arrayLength = ArrayLen(stack)>
	<cfset poppedElement = stack[#arrayLength#]> <!--- take from stack --->
	<cfset ArrayDeleteAt(stack,arraylength)><!--- delete from stack --->	
	<cfreturn poppedElement>	
</cffunction>


<!--- the function finds and displays the correct branch in the navigation --->
<cffunction name="findAndDisplayChildren" access="public" returntype="void"> <!--- doesn't return anything, only displays  --->
	<cfargument name="inputElement" type="struct" required="yes">	
	<cfargument name="inputCnt" type="numeric" required="yes">
	<CFARGUMENT name="dataSource" type="string" required="yes">
	<CFARGUMENT name="dataTable" type="string" required="yes">
	<CFARGUMENT name="breadCrumbArray" type="array" required="yes">	
	
	<!--- we need to be careful not to let the variables in the functions global...
	... can make a mess otherwise, especially with variables with same names :) --->
	<cfset var poppedElement = 0> 
	
	<!--- we query for the the shildren of the choosen parent (inputElement) --->
	<cfquery name="qryChildren" datasource="#arguments.dataSource#">
		SELECT *
		FROM #arguments.dataTable#
		WHERE parentid = #inputElement.id#
	   	ORDER BY navorder DESC		
	</cfquery>
	
	<!--- we loop through the query results and each time we make a new struture --->
	<!--- then we assign values to the structure --->
	<cfloop query="qryChildren">
		<cfset structItem = StructNew()>
		<cfset structItem.id = qryChildren.id>
		<cfset structItem.navdescription = qryChildren.navdescription>
		<cfset structItem.link = qryChildren.link>
		<cfset structItem.navorder = qryChildren.navorder>
		<cfset structItem.parentid = qryChildren.parentid>
		<cfset structItem.level = #arguments.inputCnt#>
		
		<cfset push(inputStruct = structItem)> <!--- PUSH --->
	 </cfloop> 
	 
	<!--- we loop trough the items in the same level (stack[ArrayLen(stack)].level EQ arguments.inputCnt) --->	
	<cfloop condition="stack[ArrayLen(stack)].level EQ arguments.inputCnt">	
	
		<!--- <cfdump var="#stack#"> --->
		<cfset poppedElement = pop()> <!--- POP  --->		
		
		<!--- DISPLAY OTHER THAN SOOT ITEMS - MAKE CSS ADJUSTMENTS HERE --->
		<cfoutput>
			<div style="padding-left:#poppedElement.level*20#px">			
				<a href="#poppedElement.link#">#poppedElement.navdescription#</a> 
			</div>
		</cfoutput>
			
		<!--- THE ONLY, BUT MOST IMPORTANT RECURSIVE CALL HERE --->
		<!--- we pass trough only the items that are "defined" by the "list" of parents in "bread crumb" array --->
		<cfif (inArray(element = poppedElement.id, inputArray = breadCrumbArray) IS TRUE)> 
			<cfset findAndDisplayChildren(inputElement = poppedElement, inputCnt = arguments.inputCnt+1, dataSource = arguments.dataSource, dataTable = arguments.dataTable, breadCrumbArray = arguments.breadCrumbArray)>   
		</cfif> 
		
	</cfloop>
</cffunction>




<!--- the function displays the root menu and the proper branch of submenu (children) --->
<cffunction name="displayMenu" access="public" returntype="void"> <!--- the function won't be returning anything, only displaying --->
	<cfargument name="inputItem" type="numeric" required="yes">	   
	<CFARGUMENT name="dataSource" type="string" required="yes">
	<CFARGUMENT name="dataTable" type="string" required="yes"> 
	<CFARGUMENT name="breadCrumbArray" type="array" required="yes">
	
	
	<cfset var poppedElement = 0> <!--- defining a local variable --->
	
	
	
	
	<!---  we define a new structure that will hold the data of one navigation item --->
	<cfset struct = StructNew()>
	
	<!--- we query only the root items from the table --->
	<cfquery name="qryRootItems" datasource="#arguments.dataSource#">
		SELECT *
		FROM #arguments.dataTable# <!--- defined in the main program (main variables) --->
		WHERE parentid = 0
	   	ORDER BY navorder DESC
	</cfquery>
	
	<!--- we loop trough the query results and each time we make a new struture --->
	<!--- then we assign values to the structure --->
	<cfloop query="qryRootItems">
		<cfset struct = StructNew()> <!--- make a new wtruct --->
		<cfset struct.id = qryRootItems.id>
		<cfset struct.navdescription = qryRootItems.navdescription>
		<cfset struct.link = qryRootItems.link>
		<cfset struct.navorder = qryRootItems.navorder>
		<cfset struct.parentid = qryRootItems.parentid>
		<cfset struct.level = 0>					
		<cfset push(inputStruct = struct)> <!--- PUSH - adding on stack--->
		<!--- we use the "stack mechanism" in order be able to display the navigation items correctly  --->
		
	</cfloop>
	
   <!--- we will push and pop until the stack ios empty  --->
   <!--- once the stack is empty, the whole navigation is displayed on the screen --->
   <cfloop condition="NOT ArrayIsEmpty(stack)"> 
		
		<cfset poppedElement = pop()> <!--- POP - taking from the stack --->
		
		<!--- DISPLAY ROOT ITEMS - MAKE CSS ADJUSTMENTS HERE --->
		<!--- we only display root items here --->
		<cfif poppedElement.level IS 0>
			<cfoutput>
				<div><a href="#poppedElement.link#">#poppedElement.navdescription#</a></div><!--- display the item --->
			</cfoutput>
		</cfif>
		
		<!--- we only display items with the proper parent --->
		<!--- prober parents are stored in the "breadCrumbArray" --->
		<cfif (inArray(element = poppedElement.id, inputArray = breadCrumbArray) IS TRUE)>	   
			<cfset findAndDisplayChildren(inputElement = poppedElement, inputCnt = 1, dataSource = arguments.dataSource, dataTable = arguments.dataTable, breadCrumbArray = arguments.breadCrumbArray)> <!--- this is a recursive function --->
		</cfif>
		
   </cfloop>
	
</cffunction>

</cfcomponent>




Thank you.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1