A common problem I've been having and seeing lately is dealing with components that should read files in locations they don't know about. For instance, you have component Model in cfrails, which should read a configuration file in the project that is using it. I didn't want to have to create a mapping for every project, and I didn't want to have to figure out the ../../../../etc between the two directories to pass the value in to Model. So, here's a function which takes care of the mapping for you. If you needed, it would be trivially easy to add a new parameter for base template, if you didn't want to use the
actual base template as your basis for inclusion.
It doesn't have any associated automated tests, mainly because I can't figure out how I'd test something like this. So, I tested it manually in 3 different directory structures, and it works fine for me. If you ever find a need for this, and encounter problems, I'd like to know about it - especially the directory structures your using so I can replicate and fix it.
So, without further ado, here's the code:
<cffunction name="includeRelativeToBaseTemplate"
description="Given the current template's path, includes a file relative to the base template path"
output="true"
access="private"
>
<!---
why ?
Because sometimes, even though the "current template path"
is known from getCurrentTemplatePath,
cfinclude appears to use it's _actual_ current template. For example, you have
A.cfc in dir1, and B.cfc, which extends A.cfc, but it resides in another directory. But, you
want to cfinclude a file within A.cfc relative to the file that instantiated B.cfc. Well, I didn't
find it very simple, so I created this function. =)
--->
<cfargument name="currentTemplate"
hint="The template which should be relative to."
>
<cfargument name="relativePath"
hint="The relative path to the include, from the base template."
>
<cfset var local=structNew()>
<cfset local.baseTemplate = getBaseTemplatePath()>
<cfset local.curTemplate = arguments.currentTemplate>
<cfset local.relative=arguments.relativePath>
<!--- in case of unix, convert slashes --->
<cfset local.baseTemplate = replace(local.baseTemplate,"\"
,"/"
,"all"
)>
<cfset local.curTemplate = replace(local.curTemplate,"\"
,"/"
,"all"
)>
<cfloop from="1"
to="#max(listlen(local.baseTemplate,'/'),listlen(local.curTemplate,'/'))#"
index="local.i"
>
<cfset local.baseDir=listGetAt(local.baseTemplate,local.i,"/"
)>
<cfset local.curDir=listGetAt(local.curTemplate,local.i,"/"
)>
<cfif local.baseDir neq local.curDir>
<cfbreak>
</cfif>
</cfloop>
<cfset local.goBackTimes=listlen(local.curTemplate,'/')-local.i>
<cfset local.dotDotSlash = repeatString("../"
, local.goBackTimes)>
<cfset local.newPath=listDeleteAt(local.baseTemplate,listLen(local.baseTemplate,"/"
),"/"
)>
<cfloop from="1"
to="#local.i-1#"
index="local.k"
>
<cfset local.newPath = listDeleteAt(local.newPath,1
,"/"
)>
</cfloop>
<cfif listlen(local.baseTemplate,'/') lt listlen(local.curTemplate,'/')>
<cfset local.dotDotSlashesInRelative = listvaluecount(local.relative,".."
,"/"
)>
<cfloop from="1"
to="#local.dotDotSlashesInRelative#"
index="local.k"
>
<cfset local.newPath = listDeleteAt(local.newPath,listLen(local.newPath,"/"
),"/"
)>
</cfloop>
<cfset local.relative = replace(local.relative,"../"
,""
,"all"
)>
</cfif>
<cfset local.newPath = local.dotDotSlash & local.newPath & "/"
& local.relative>
<!---if fileExists(expandPath(local.newPath)) - cf using different relative path than current? --->
<cftry>
<cfinclude template="#local.newPath#"
>
<cfset result = true>
<cfcatch>
<cfset result = false>
</cfcatch>
</cftry>
<cfreturn result>
</cffunction>
Hey! Why don't you make your life easier and subscribe to the full post
or short blurb RSS feed? I'm so confident you'll love my smelly pasta plate
wisdom that I'm offering a no-strings-attached, lifetime money back guarantee!
Leave a comment
There are no comments for this entry yet.
Leave a comment