Extend a CFC using a relative path - coldfusion

I want to extend a CFC in a different directory and I have a couple of options, but can't figure out how to do this:
A) Use a dynamic mapping (this will have to be dynamic based on the site, e.g. for the live site it would be cfc.myPackage.MyCFC but on a dev site it would be myCfcRoot.myPackage.MyCFC) - I've tried putting expressions into the extends bit but obviously CF doesn't like that, e.g. :
<cfcomponent name="MyComponent" extends="#config.cfcRoot#.BaseComponent">
or
<cfcomponent name="MyComponent" extends="#GetRealPath(../BaseComponent.cfc)#">
B) Provide a relative path (somehow) to the CFC to extend.
I fear that I can't do this, but I'm hoping that there is something I've missed.

Daniel is basically correct, you need a mapping. However, there are 3 workarounds.
CFCs will pick the current path as a relative root mapping, so if your CFCs are all in the same directory, you can just say
<cfcomponent name="MyComponent" extends="Example">
Or if your components are in subdirectories from the current cfc, you can access it:
<cfcomponent name="MyComponent" extends="subdirectory.Example">
Second, if you are running on ColdFusion 8, you can define a mapping in your application.cfc using the mappings struct like this:
<cfset this.mappings["/MyApp"] = expandPath(".") />
There are two good references for Application.cfc, first, Ray Camden's example Application.cfc which just gives a nice view of what goes where, then the CF8 Live Docs application settings page, which has a section on mappings along with some good comments.
Finally, you can use the built-in mapping of your web root, so if your application is in a subdirectory named "MyApp" off the web root, your root mapping will be "MyApp". Let's say you correctly put your components in:
wwwroot\MyApp\com\MyApp\example.cfc
The mapping to this cfc in this case will be:
MyApp.com.MyApp.Example
And using your example, you can extend like this:
<cfcomponent name="MyComponent" extends="MyApp.com.MyApp.Example">
Anything else, such as if your components are outside of the web root, or if you are not sure what the folder structure of your finished application will be, and you will need to set a mapping in the CF Administrator.

Now this is only tested in cf8 so other engine could differ.
if you want to use relative paths to extend applications you can but your have to start them with a "/.". For instance you can do this to extend an application.cfc from your web root into directory below you webroot:
<cfcomponent output="false" extends="/.application">
<!--- whatever code you have --->
</cfcomponent>
now let's say I have the following paths in my application:
[webroot]/1/1a
[webroot]/2
let's say that the application.cfc in [webroot]/1/1a extends the application.cfc in [webroot]. now I want to place an application.cfc in [webroot]/2 and extend the application.cfc in [webroot]/1/1a. all i would have to do in my [webroot]/2/application.cfc is the following:
<cfcomponent output="false" extends="/./1/1a/application">
<!--- whatever code you have --->
</cfcomponent>
hope this makes sense.

Unless the CFC is in the same directory as the calling script the CFC must be located and referenced from a path relative to a "Mapping".
I have found that sometimes you need to make sure that "/" is mapped to your document root of your webserver and that becomes the base for all relative paths. Or you can setup a mapping for a CFC directory that hold all of your common CFCs.
This screen is found in the ColdFusion Admin under "Server Settings" -> "Mappings".

In regards to Method B. It can be done using an additional file in your example. It's one extra file per cfc that you want to extend per directory that you want to extend from.
The 3 files that are required.
/somepathtobasecomponent/basecomponent.cfc (your base component file)
/pathtoworkingcfc/function.cfc (the file with the extends attribute)
/pathtoworkingcfc/basecomponent_extend.cfc (the new file that will do the extending)
/somepathtobasecomponent/basecomponent.cfc
Nothing to change here. This stays the same.
/pathtoworkingcfc/function.cfc
in the cffunction tag set the extends to extends="basecomponent_extend"
/pathtoworkingcfc/basecomponent_extend.cfc
Sole content of the file is a cfinclude
<cfinclude template="/somepathtobasecomponent/basecomponent.cfc">

Related

How to traverse two levels up when referencing a coldfusion component in a cfinvoke?

If I don't want to do any mappings, is there another way to call a method in a component, which is "two levels up". Like so:
service
component_to_call.cfc
mem
pages
handlers
calling.cfc
this is my call from inside calling.cfc:
<cfinvoke
component="service.component_to_call"
method="do_the"
param1="#renderedResults#"
param2="#taskByName#"
returnvariable="tamperedCode">
</cfinvoke>
which throws an error that the service.component_to_call can't be found.
Qustion:
How can I call the component in the parent folder?
Why don't you want to use mappings? That's a bit odd.
Anyway, just browse directly to the CFC you wish to instantiate, and it will output it's autogenerated API docs, the first thing will be its full dotted path. You can use that. However that is absolute, not relative.
What you cannot do is to somehow specify a relative path like one might with a file (eg: "../../fileInGrandparentDir.cfm"... one cannot do that with a CFC path.
An alternative is to make some directory that is an ancestor to both CFCs into a custom tag path, then you can just reference the CFCs by their filename and CF will find them. There's a performance hit with doing this if the directory structure is complex. That said: not much of a performance hit.
Really... you should have a mapping to the top level of your app's directory, and then use the mapping to fully-path the CFCs. That's how it's generally done, and is the most transportable approach, I think.
You could include the CFC from the level up in a Proxy.cfc that is at the same level as the calling CFC and then extend Proxy.cfc by adding extends="Proxy" as an attribute of the calling CFC.
Example Proxy.cfc
<cfcomponent name="Proxy">
<cfinclude template="../../service/component_to_call.cfc">
</cfcomponent>
Example calling cfc
<cfcomponent name="calling" extends="Proxy">
</cfcomponent>

Mapping to a CFC in ColdFusion

In my application I have all my CFC's in a cfc folder. From the site root I can access them without any trouble by simply referring to them in my <cfinvoke> tag as component=cfc.mycomponent method=mymethod
The trouble is, when I want to access the cfc from another page that's not in the root I can't use component=../.cfc.mycomponent to get in touch with that cfc.
What am I doing wrong here?
There are a handful of options for getting this to work. Unfortunately, learning them has taken me a good amount of trial and error. Let me share what I've learned.
First, you can use the classic method of creating a mapping in your CF Administrator. Specify the exact path to your components (e.g. c:\wwwroot\cfc), and the mapping (pseudo-folder) that you want to call it by (e.g. MyCFCs). Now from anywhere in your application, you can reference create a new MyCFCs.mycomponent() (using CF9+'s new keyword, you can substitute for createObject("component","MyCFCs.mycomponent") to be compatible back to CF6).
The downsides to using a server mapping are that you have to configure this on every server your application runs on. I typically have a local development server which has a radically different configuration from my production servers, and making changes on production servers is a pain for me, so I try to avoid server mappings whenever possible.
Second, you can reference your CFCs from a web-root-relative path, meaning that if your application is in the root of your server and the /cfc path is directly off of the web root, you can always do new cfc.mycomponent() from anywhere in your application. ColdFusion 6.1 and up will correctly map to the root of your web site. This is like referencing an image using /images/mypicture.jpg, anywhere in your web site, /images will will go straight to the same directory.
The downside of using the web-root-relative path is that if your application will ever be in a different folder off of the web root, or will ever be in a subdirectory and sometimes be at the web root, the relative path from the web root will change, breaking these links.
Third, you can create an application-specific mapping. This was introduced in CF8 and requires that you have an Application.cfc file. It is simple to add. Raymond Camden has a great reference. The syntax is essentially like this.
<cfset this.name = "MyAppName"/>
<cfset this.mappings = structNew() />
<cfset this.mappings["/cfc"] = getDirectoryFromPath(getCurrentTemplatePath()) & "cfc/" />
The only downside to this method is that your Application.cfc can't extend a CFC in a mapped folder. It's an obscure problem, which probably won't affect you. Also, you will need to have an Application.cfc, which is good practice, but I don't know if you are doing that yet.
Fourth, you can instantiate your CFC into your application scope, probably from within the aforementioned Application.cfc, inside an OnApplicationStart() method. This moves any compile/instantiation time into your application's first hit, and removes it from subsequent hits. The code is very simple.
<!--- from Application.cfc, inside onApplicationStart() --->
<cfset application.myComponent = new cfc.myComponent() />
<!--- from anywhere else in your application --->
<cfset application.myComponent.callMyMethod() />
The downside to this one is that once your component is in Application memory, any changes you make to it while you are developing your application will not be reflected until you clear the application memory or call onApplicationStart() again. It's not hard to get around, but it's just more code, and more to manage.
One final note, you may want to think about moving from <cfinvoke> to createObject("component",...) or, if you are on CF9, new. The cfinvoke syntax is fine, but every time you invoke a component from a path, you are re-instantiating it, and it also is not a very object-oriented way to call your components. Food for thought, take it or leave it :)
You can't use relative paths with components.
What you need is a ColdFusion mapping. There are two ways to do this.
The first is to go into your ColdFusion administrator, go into the mappings section and add a /cfc mapping that points to your cfc folder.
The other way is to use application specific mappings;
In the Application.cfc for your application you can add application mappings as you would under the ColdFusion administrator mapping. At the top of your application cfc add a cfset of this.mappings as an array. In this array set the mapping with the directory path.
<cfset this.mappings["/cfc"] = GetDirectoryFromPath( GetCurrentTemplatePath() )&"cfc">
with the mapping of /cfc to your cfc folder any component calls to cfc.objectname will load the appropriate component in your cfc folder.
You absolutely can extend a cfc with mappings. I had to do it myself.
One of the most frustrating things that I have had to deal with in ColdFusion is trying to create an external application that is open to the general public and having to secure a portion of that site with an application within a subfolder and extending the logic from base application.cfc. I'll walk you through the current approach that developers use to solve this as well as showing you how to additionally use mapping when there may be a hosting provider that uses virtual directories.
This is a rather long article, if you want to jump to the condensed summary, scroll down to the bottom of this page.
Many years ago, the first time that I tried to perform this, I received the following message no matter what I tried: "Could not find the ColdFusion component or interface xxx'. In a nutshell, the problem using this approach is that both the root and the subfolders have the same name, i.e. Application.cfc, and ColdFusion can't properly identify what component to extend. Finally, after some serious investigation, someone came up with the idea to create a proxy.cfc that resides in the same root directory as the root Application.cfc, and the Application.cfc in the subfolder extends an empty proxy.cfc that extends the root cfc like so:
root directory: Application.cfc
This root Application.cfc does not extend anything
Also in the root directory: Proxy.cfc
Proxy.cfc has the following code, it's essentially empty. The only thing that the Proxy.cfc does is to extend the Application.cfc that is in the same directory:
<cfcomponent extends="Application">
</cfcomponent>
Subdirectory such as a folder named admin.
This subdirectory has another Application.cfc. Let's say that this component is responsible for securing the application and has login logic as well as debugging settings for example. This Application.cfc will extend the Proxy.cfc to gain the methods and properties of the Application.cfc in the root directory like so:
<cfcomponent displayname="Admin" extends="Proxy.cfc">
This approach was a godsend and it was heavily blogged about. Ben Nadel has made a number of very helpful posts which I will share at the bottom of this article.
This works quite well unless you're on a hosted domain or a server that uses virtual directories. In this case, we are in the same original boat in which we started from. Now we are back into the "Could not find the ColdFusion component or interface xxx' hell!
There is a solution for this tricky problem though, we need to also use mapping!
It is a common misnomer that you can't use mapping to extend components. I am not quite sure where this misconception originally came about, but it has been proven that this is just not true. There are occasions where we must use mapping to solve some annoying problems, like here.
This particular site is hosted by hostek.com. They are a fine company to deal with, but the server that my site is hosted on has some idiosyncrasies due to the directory structure. Here, when I use the Proxy.cfc method to extend the logic from the base Application.cfc to the Application.cfc in the admin folder I receive the dreaded 'could not find the ... component' error. When I first saw it I was dismayed thinking not this again, so I turned to ColdFusion CFC mapping. Mapping tells ColdFusion where to find the file and what the file relationships are.
Let's review CFC structure that was just discussed. For example, imagine the following directory structure:
root directory: i.e. www.gregoryalexander.com/
subdirectory: www.gregoryalexander.com/admin/
As discussed, we have an Application.cfc and the Proxy.cfc in the root directory, and we have the Application.cfc in the 'admin' subdirectory.
The Proxy.cfc extends the Application.cfc, also in the root directory, and the Application.cfc in the subdirectory (admin) extends the Proxy.cfc in the root directory.
root directory: contains both Application.cfc and Proxy.cfc (that extends the root Application.cfc).
subdirectory: Application.cfc (that extends Proxy.cfc).
Now we need to also add the following mapping in the root Application.cfc. This mapping logic should be near the top of the root Application.cfc, and it should not be within any of the Application.cfc event handlers (onApplicationStart, onApplicationRequest, etc). This mapping code does not need to be anywhere else other than the root Application.cfc:
<cfset this.mappings = structNew() />
<cfset this.mappings["rootCfc"] = getDirectoryFromPath(getCurrentTemplatePath()) />
<cfset this.mappings["adminCfc"] = getDirectoryFromPath( getCurrentTemplatePath() & "/admin" ) />
I used rootCfc to identify the Application.cfc in the root directory, whereas adminCfc applies to the Application in the admin directory. These variables can be named anything. Note that the "/admin" string at the end of the adminCfc mapping points to the 'admin' folder, which is a subdirectory.
Now that we have the mappings in the root Application.cfc, we need to apply them to the extends statement in Application.cfc located in the subdirectory. In the /admin/Application.cfc template use:
/admin/Application.cfc
<cfcomponent displayname="xxx" sessionmanagement="xx" clientmanagement="xx" extends="rootCfc.Proxy">
Of course, rootCfc tells the Application.cfc in the subdirectory to look for the Proxy.cfc template in the root directory. Like other 'extend' statements, you don't need to specify '.cfc' at the end of Proxy.
You don't need to use this 'extend' mapping in either the root Proxy.cfc or Application.cfc templates. They can already find each other as they are both in the same root directory.
/Proxy.cfc
<cfcomponent extends="Application">
</cfcomponent>
Summary
For the sake of absolute clarity:
root Application.cfc
Contains the mapping logic. Has the mappings for both of the root and subdirectory.
Does not use an 'extend' statement
<cfset this.mappings = structNew() />
<cfset this.mappings["rootCfc"] = getDirectoryFromPath(getCurrentTemplatePath()) />
<cfset this.mappings["adminCfc"] = getDirectoryFromPath( getCurrentTemplatePath() & "/admin" ) />
root Proxy.cfm
A simple 'extends="Administrator" works.
No mapping logic.
<cfcomponent extends="Application">
</cfcomponent>
subdirectory Application.cfc
The extends statement must be the mapping variable name of the folder (rootCfc), a dot (.), and finally the name of the Proxy.cfc template without the .cfc prefix (Proxy)
<cfcomponent displayname="Admin" sessionmanagement="yes" clientmanagement="yes" extends="rootCfc.Proxy">
My apologies for being so verbose. I annoyed myself while writing this- but not as annoyed when I was while trying to solve this problem!
Take care!
Make sure if your components extend other components the full path is used.
ie a Products controller
<cfcomponent displayname="Products" hint="Handles all product requests" extends="core.controller.controller" output="false">

CFC extends sibling folder

I've seen all kinds of solutions for extending cfcs in parent folders with access to parent files or the CF administration, but I have not seen a workable solution to extend a cfc in a "shared"/sibling folder without access to parent folders.
This solution requires access to parent folders? (Not sure what these mappings are, but I have no access to Application.cfc anyway)
This solution requires being able to create an application.cfc which doesn't work for me (creating one in myApp does nothing because the environment I'm in includes the index page in myApp and builds the from there... the client never directly calls it to fire recognition of Application.cfc)
For instance:
wwwroot/some/path/myApp/Shared/Base.cfc
wwwroot/some/path/myApp/Function/Function.cfc
I'm looking to be able to call functionality in the Base.cfc (which contains common methods used in the application) from the Function.cfc via super and extension.
I have access to all files within myApp, but not "wwwroot", "some", or "path."
In order to extend Base.cfc within Function.cfc I have to extend the full path of "some.path.myApp.Shared.Base" This would cause problems if someone renamed the myApp folder since I would have to hand edit every Function.cfc that extends that Base.cfc
What I'm looking to do it create an application specific "barrier" so if the app folder name changes it will not cause mass havoc that requires I edit all the CFC files that use functionality from the Base.cfc.
Since I can't use relative paths to the Base ("..Shared.Base") I'm wondering if there's a way to create a CFC in the myApp folder that I can extend from and alleviate the renaming headache if it were to occur or a way to give it a generic name like "myApp" and extend from there. (myApp.Shared.Base)
I do not have access to Application.cfm, nor the Coldfusion administration.
Personally I would go simpler way: incapsulate the Base into the Function.
Looks like you want to use the set of core components for some common functionality. If this is true -- incapsulation even more applicable.
Paths to objects can be built dynamically, for example (step-by-step process for easier reading):
<cfscript>
path1 = GetDirectoryFromPath(cgi.SCRIPT_NAME);
path2 = ListToArray(path1, "/");
path3 = path2;
path3[ArrayLen(path3)] = "shared";
path4 = ArrayToList(path3, ".");
path5 = ArrayToList(path2, ".");
myBase = CreateObject("component", "#path4#.Base");
myFunction = CreateObject("component", "#path5#.Function").init(myBase);
</cfscript>
In the Function create function init:
<cffunction name="init">
<cfargument name="base">
<cfset variables.Base = arguments.base />
<cfreturn this />
</cffunction>
Of course, you may have strong reasons for extending, but at least this approach is not sensitive to the parent directories renaming.
If Base.cfc doesn't extend another cfc then you may be able to include the Base.cfc file into another cfc file in your function folder.
For example create a cfc file in the function folder with content:
<cfinclude template="../shared/base.cfc" />
Then extend the new file instead of the cfc in the shared folder.
The easiest way I can see to do this is to create a symlink or junction point to the base directory in the extension function directory.
Unfortunately, it's not a pure CF solution, nor portable if you need to move your code. Hopefully, someone will have a better answer, but this could be a fallback point if none is presented.
Generate the code on app start/reset...
For example the component tag could be like so in a file that should not be run directly...
<cfabort>
<cfcomponent extends="{sharedpath}.Base">
...
then in a function in or called from application.cfc do something like this...
<cfdirectory name="codetemplates" action="list" directory="wwwroot/some/path/myApp/codetemplates" />
<cfloop query="codetemplates">
<cffile name="temp" action="read" path="#tempfilepath##filename#" />
<cfset newfilecontent = replace(temp.filecontent, '{sharedpath}', configvarwithrightpath) />
<cfset filecontent = replace(newfilecontent , '<cfabort>', '') />
<cffile name="temp" action="write" path="#livefilepath##filename#" />
</cfloop>
The other approach is to have a build process locally that you run to create the necessary code but my example above would allow you to update a config variable and update the cfc's as needed in a live environment.
Why bother putting the shared code in a separate folder? If you just put it in the same folder as your "functions" cfcs then they can all extend it using a relative path.
So instead of:
wwwroot/some/path/myApp/Shared/Base.cfc
wwwroot/some/path/myApp/Function/Function.cfc
Use:
wwwroot/some/path/myApp/Function/Base.cfc
wwwroot/some/path/myApp/Function/Function.cfc
and:
<cfcomponent extends="Base"></cfcomponent>
However, if you have/need multiple "function"-level folders, then you can do something similar. Put a Proxy.cfc inside each "function"-level folder, with this code:
<cfcomponent extends="some.path.myApp.shared.Base"></cfcomponent>
And then each cfc in the "function"-level folders would extend their local Proxy.cfc (extends="proxy"). This gives you 1 Proxy per folder, which is still not ideal, but less of a hassle than updating every cfc.

Fusebox installation without access to webroot

Previously (and locally) I've placed the fusebox5 directory in the web root, and then applications from anywhere in the tree have been able to access it. I'd previously also used Application.cfm rather than .cfc.
In this environment I don't have access to the webroot and the FB files don't really need to be that far down anyway, so I had planned to store them alongside the applications. Am I right in thinking that index.cfm is overlooked if Application.cfc is in use (and therefore there's no point changing the cfinclude value to be eg. ../fusebox5/)? If so, how can I include the framework without having Fusebox in the root or in a mapping? Error is:
Could not find the ColdFusion Component or Interface fusebox5.Application.
No, your app is still going to need index.cfm. What you'll need is a cf application mapping for Fusebox in your Application.cfc. Look at Ray Camden's Application.cfc template for an example of setting application specific mappings.
You can run Fusebox 5+ in a subfolder of your app root. It just may not be the most obvious thing to make it work.
Unfortunately, you cannot create a dynamic mapping for extending Application.cfc because your Application.cfc has not yet been instantiated - you get a chicken vs. egg scenario. You can't create the mapping to Fusebox because your Application.cfc didn't start, you can't start your Application.cfc because it can't find the component it's supposed to extend.
THIS IS A BUG IN COLDFUSION 8. ColdFusion should look for mappings in this order:
Mapped folders from the CF Administrator
Sub directories off the current directory
Sub directories off the web root called
It does this when you use CreateObject(), but not when you use the Extends attribute on cfcomponent.
The easiest solution is to use your Application.cfc like you would for any application, then include fusebox from your index.cfm. Your folder structure would look like this:
/myapp/fusebox5/
/myapp/index.cfm
-- consists of <cfinclude template="fusebox5/fusebox5.cfm" />
Your index.cfm file will not be ignored as long as you don't intercept the request with Application.cfc's OnRequest, or if you use OnRequest, make sure you include the intended target (which will almost always be index.cfm anyway).
If you want to not require index.cfm to do the include, you can have your Application.cfc's OnRequest method do the cfinclude.
<cffunction name="onRequest">
<cfinclude template="fusebox5/fusebox5.cfm">
</cffunction>
You still may need an index.cfm so your web server won't give a directory listing or 404, but it's ok if the file is empty.
In Application.cfc:
<cfscript>
this.mappings = {}; //create a new structure to store app-specific mappings
this.mappings["Fusebox"] = expandPath('./Fusebox'); //add mapping
</cfscript>

Extending application.cfc in a subdirectory

I have the following two files and would like the second to extend the first:
wwwroot\site\application.cfc
wwwroot\site\dir\application.cfc
However, when I go to declare the component for the second file, I'm not sure what to put in the extends attribute. My problem is that several dev sites (with a shared SVN repository) are running off the same instance of ColdFusion, so I can't just create a mapping in the CF admin like so:
<cfcomponent extends="site.application">
However, ColdFusion doesn't like:
<cfcomponent extends="..application">
or any dynamic input like:
<cfcomponent extends="#expandpath('..').#application">
Creating a runtime mapping (like here) doesn't seem possible either. Creating it in the base application.cfc is useless because that code hasn't yet executed by the time the inheriting cfc is being declared; and I can't create the mapping before the inheriting component is defined because there isn't yet an application to attach it to.
Is there any way I can reference the parent directory to accomplish my extends?
Edit to clarify: The ApplicationProxy solution doesn't work because of the bolded text above. Right now, as a workaround, we're simply not checking the \dir\application.cfc into SVN so that each developer can keep a version that extends his/her own root application.cfc. Obviously, this is not ideal.
Sean Corfield has a blog entry explaining how to extend a root Application.cfc.
Below is the relevant information copied from that entry.
Here's your root CFC /Application.cfc:
<cfcomponent>
<cfset this.name = "cf7app" />
<cfset this.sessionmanagement = true />
</cfcomponent>
Here's your proxy CFC /ApplicationProxy.cfc:
<cfcomponent extends="Application">
</cfcomponent>
It's completely empty and serves merely to create an alias for your root /Application.cfc. Here's your subdirectory CFC /app/Application.cfc:
<cfcomponent extends="ApplicationProxy">
<cffunction name="onSessionStart">
<cfoutput><p>app.Application.onSessionStart()</p></cfoutput>
<cfset session.counter = 0 />
</cffunction>
<cffunction name="onRequestStart">
<cfoutput><p>app.Application.onRequestStart()</p></cfoutput>
<cfdump label="application" var="#application#"/>
</cffunction>
</cfcomponent>
The root of each individual site should have its own Master App:
/site1/Application.cfc
/site2/Application.cfc
/site3/Application.cfc
All these applications are separate individual apps with nothing shared between them.
If any of these individual sites need to have sub-applications, then there should be ApplicationProxy.cfc alonside the Master,
e.g.
/site1/ApplicationProxy.cfc
/site2/ApplicationProxy.cfc
Then, for each sub-application you have the one that extends the proxy:
e.g.
/site1/subA/Application.cfc
/site1/subB/Application.cfc
/site2/subA/Application.cfc
The following code is working for me. One thing I noticed though is that the application.cfc seems to get cached, so changes to the parent application cfc might not be reflected. I got around this by doing a trivial change to the child application cfc.
<cfcomponent output="false">
<cfset variables.higherPath = ReReplace(GetMetaData(this).name,"\.[^\.]+\.[^\.]+$","") />
<cfset variables.extendApp = CreateObject("component", "#variables.higherPath#.Application") />
<cfloop item="variables.key" collection="#variables.extendApp#">
<cfif IsCustomFunction(variables.extendApp[variables.key])>
<cfset super[variables.key] = variables.extendApp[variables.key]>
<cfelse>
<cfset this[variables.key] = variables.extendApp[variables.key] >
</cfif>
</cfloop>
<cffunction name="onApplicationStart" output="false">
<cfset super.onApplicationStart() />
</cffunction>
I know this is an old topic, but I found a way to do it (that seems to work in my testing) without using the CF Administrator mappings.
You can do this by adding a per-application mapping in your child Application.cfc using an expanded relative path:
<cfcomponent extends="cms.Application" output="false">
<cfset this.mappings["/cms"] = expandPath(getDirectoryFromPath(getCurrentTemplatePath()) & "../../../../")>
<cflog text="#getMetadata(this).extends.path#">
</cfcomponent>
Yeah, it feels little hacky, but it seems to work.
Edward, et-al, I referred to your comment in the post below. See https://gregoryalexander.com/blog/2021/1/30/Extending-Applicationcfcs-using-mappings-and-proxies
You absolutely can extend a cfc with mappings. I had to do it myself.
One of the most frustrating things that I have had to deal with in ColdFusion is trying to create an external application that is open to the general public and having to secure a portion of that site with an application within a subfolder and extending the logic from base application.cfc. I'll walk you through the current approach that developers use to solve this as well as showing you how to additionally use mapping when there may be a hosting provider that uses virtual directories.
This is a rather long article, if you want to jump to the condensed summary, scroll down to the bottom of this page.
Many years ago, the first time that I tried to perform this, I received the following message no matter what I tried: "Could not find the ColdFusion component or interface xxx'. In a nutshell, the problem using this approach is that both the root and the subfolders have the same name, i.e. Application.cfc, and ColdFusion can't properly identify what component to extend. Finally, after some serious investigation, someone came up with the idea to create a proxy.cfc that resides in the same root directory as the root Application.cfc, and the Application.cfc in the subfolder extends an empty proxy.cfc that extends the root cfc like so:
root directory: Application.cfc
This root Application.cfc does not extend anything
Also in the root directory: Proxy.cfc
Proxy.cfc has the following code, it's essentially empty. The only thing that the Proxy.cfc does is to extend the Application.cfc that is in the same directory:
Subdirectory such as a folder named admin.
This subdirectory has another Application.cfc. Let's say that this component is responsible for securing the application and has login logic as well as debugging settings for example. This Application.cfc will extend the Proxy.cfc to gain the methods and properties of the Application.cfc in the root directory like so:
This approach was a godsend and it was heavily blogged about. Ben Nadel has made a number of very helpful posts which I will share at the bottom of this article.
This works quite well unless you're on a hosted domain or a server that uses virtual directories. In this case, we are in the same original boat in which we started from. Now we are back into the "Could not find the ColdFusion component or interface xxx' hell!
There is a solution for this tricky problem though, we need to also use mapping!
It is a common misnomer that you can't use mapping to extend components. I am not quite sure where this misconception originally came about, but it has been proven that this is just not true. There are occasions where we must use mapping to solve some annoying problems, like here.
This particular site is hosted by hostek.com. They are a fine company to deal with, but the server that my site is hosted on has some idiosyncrasies due to the directory structure. Here, when I use the Proxy.cfc method to extend the logic from the base Application.cfc to the Application.cfc in the admin folder I receive the dreaded 'could not find the ... component' error. When I first saw it I was dismayed thinking not this again, so I turned to ColdFusion CFC mapping. Mapping tells ColdFusion where to find the file and what the file relationships are.
Let's review CFC structure that was just discussed. For example, imagine the following directory structure:
root directory: i.e. www.gregoryalexander.com/
subdirectory: www.gregoryalexander.com/admin/
As discussed, we have an Application.cfc and the Proxy.cfc in the root directory, and we have the Application.cfc in the 'admin' subdirectory. The Proxy.cfc extends the Application.cfc, also in the root directory, and the Application.cfc in the subdirectory (admin) extends the Proxy.cfc in the root directory.
root directory: contains both Application.cfc and Proxy.cfc (that extends the root Application.cfc).
subdirectory: Application.cfc (that extends Proxy.cfc).
Now we need to also add the following mapping in the root Application.cfc. This mapping logic should be near the top of the root Application.cfc, and it should not be within any of the Application.cfc event handlers (onApplicationStart, onApplicationRequest, etc). This mapping code does not need to be anywhere else other than the root Application.cfc:
I used rootCfc to identify the Application.cfc in the root directory, whereas adminCfc applies to the Application in the admin directory. These variables can be named anything. Note that the "/admin" string at the end of the adminCfc mapping points to the 'admin' folder, which is a subdirectory.
Now that we have the mappings in the root Application.cfc, we need to apply them to the extends statement in Application.cfc located in the subdirectory. In the /admin/Application.cfc template use:
/admin/Application.cfc
Of course, rootCfc tells the Application.cfc in the subdirectory to look for the Proxy.cfc template in the root directory. Like other 'extend' statements, you don't need to specify '.cfc' at the end of Proxy.
You don't need to use this 'extend' mapping in either the root Proxy.cfc or Application.cfc templates. They can already find each other as they are both in the same root directory.
/Proxy.cfc
Summary
For the sake of absolute clarity:
root Application.cfc
Contains the mapping logic. Has the mappings for both of the root and subdirectory.
Does not use an 'extend' statement
root Proxy.cfm
A simple 'extends="Administrator" works.
No mapping logic.
subdirectory Application.cfc
The extends statement must be the mapping variable name of the folder (rootCfc), a dot (.), and finally the name of the Proxy.cfc template without the .cfc prefix (Proxy)
My apologies for being so verbose. I annoyed myself while writing this- but not as annoyed when I was while trying to solve this problem!
Take care!