Extending application.cfc in a subdirectory
Asked Answered
M

4

13

I have the following two files and would like the second to extend the first:

  1. wwwroot\site\application.cfc
  2. 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.

Mess answered 21/11, 2008 at 0:41 Comment(0)
A
4

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>
Aurelio answered 21/11, 2008 at 21:42 Comment(0)
U
16

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
Unionist answered 21/11, 2008 at 0:49 Comment(8)
Doesn't this assume that the site root is the web root? It doesn't seem to work for me.Mess
Soldernal, if the site root is not the web root, you will need a mapping to the site root. If your mapping name is foo, then you extend "foo.application".Eating
It seems like I just run into my original problem again, though. If I create a mapping for foo to \devsite1\, then \devsite2\dir\application.cfc will extend \devsite1\application.cfcMess
Don't create the mapping to /devsite1. Create a /common mapping, and refer to common.ApplicationProxy in both /devsite1/application.cfc and /devsite2/dir/application.cfcUnionist
I'm not following you. Wouldn't this just mean the /dir/application.cfc files are extending a common application.cfc? The whole point is so that I can edit \application.cfc without having to worry about breaking other dev sites while doing so.Mess
I just edited the answer to try and explain how this would work for multiple sites with their own sub-apps, although I'm still not sure if I understand what you're trying to achieve.Unionist
I believe you should change your name property to DisplayName, Name is not a property of the cfcomponent tag. At least not according to the Adobe documentation. Maybe I missed something?Rebarebah
The code was just copied from the referenced link, but you're right that name isn't a valid attribute (I think it's a hangover from CFMX6/7 days). However displayname is equally useless, even though it's a supported attribute there's no need for it.Unionist
A
4

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>
Aurelio answered 21/11, 2008 at 21:42 Comment(0)
M
3

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.

Moyers answered 8/3, 2010 at 16:50 Comment(1)
This doesn't seem to work for me. Could you explain it further and maybe list what version of ColdFusion you're using? It seems like the mapping won't be defined until after the Application.cfc begins to be processed, which can't happen because the mapping is required for it to get past the 1st line.Insanitary
C
0

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!

Comedo answered 30/1, 2021 at 12:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.