ColdFusion 8 Application Mappings and Sub-folders

2008 February 27
tags: ColdFusion · Learning
by Paul Marcotte
Now that I'm on a CF8 hosting plan, I've started leveraging some of that goodness in my applications. The first thing I have implemented is application level mappings to keep my configuration files and components out of the web root. Here's how my current project layout rolls.
/project
  /config <-- Coldspring and Transfer config files
  /model <-- model components
  /root <-- web root
    /admin  <-- secure admin screens
Basically, I want to create mappings to config and model. I did a little googlin' and found a nice snippet on Sean Corfield's blog on looping over a list to build the mappings. I aped that code with a slight mod for my setup. <cfif (ListLast(CGI.HTTP_HOST,".") eq "local")>
<cfset mapPath = "../">
<cfelse>
<cfset mapPath = "../" & this.name & "/">
</cfif>

<cfloop index="p" list="config,model">
<cfset this.mappings["/" & p] = expandPath(mapPath) & p />
</cfloop>
I dev on Mac and deploy on Windows and my local folder structure is slightly different from my host. The first part of the code is just a test of whether the site is being served up locally. When on the production server, the mapped folders are in a folder with the same name as the app. This is a great alternative to using Apache Alias or Virtual Directories in IIS for mappings. That is, until I tried using the mapping from within the admin folder. Suddenly the path was not recognized. I initially thought this was a transfer issue, but that was not the case. I tried several variations and workarounds. Eventually, I figured out that the mappings were fine for files in the web root where Application.cfc resides. So why was the path not available to files in a folder below the web root? The answer was pretty simple. The admin folder had no Application.cfc, thus no mappings. Remembering another gem by Mr. Corfield regarding extending Application.cfc via a Proxy, I created an Application.cfc in the admin folder and the required proxy in the web root. Instead of repeating the code for creating the mappings with only the path prefix changed, I wrapped the creation of mappings up in a method. <cffunction name="createMappings" access="private" output="false" returntype="void">
   <cfargument name="pathPrefix" type="string" required="false" default="../">
   <cfif (ListLast(CGI.HTTP_HOST,".") eq "local")>
      <cfset variables.mapPath = arguments.pathPrefix>
   <cfelse>
      <cfset variables.mapPath = arguments.pathPrefix & this.name & "/">
   </cfif>
   <cfloop index="p" list="config,model">
<cfset this.mappings["/" & p] = expandPath(variables.mapPath) & p />
   </cfloop>
</cffunction>
And call it from the Application.cfc in the admin folder using the appropriate prefix. <cfcomponent displayname="Application" extends="ApplicationProxy">

<!--- set mappings in this directory --->
<cfset createMappings(pathPrefix="../../")>

</cfcomponent>
Application level mappings are handy, but the per directory limitation does not make for a tad more code. Overall, I think it's a small price to pay for the benefit gained.

UPDATE:

After testing this on my Windows machine, it appears that using "this.mappings" inside a method does not properly set the mappings. I've changed things around so that the createMappings() method returns a struct and I set it as a pseudo-constructor. <cfset this.mappings = createMappings(pathPrefix="../") >