Cmaso
Cmaso

Reputation: 1113

How to extend application.cfc without overwriting parent functions?

I've done some searching online to see if this can be done, and haven't found anything that says it can. I have an application.cfc at the root, and have a few subfolders where I want its functions to be the same with just minor differences. For example, the cfc at the root has a function in onRequestStart() that incorporates a function to determine the relative root path for a called template (i.e., whether it should be "../" or "../../", etc., see http://www.bennadel.com/blog/1655-ask-ben-dynamic-web-root-and-site-url-calculations-in-application-cfc.htm).

I want this to run on all pages in all subfolders, but in one subfolder I additionally want to run a security scheme on every request that checks to see if the user has valid permissions to view pages in that subfolder. If I create an application.cfc in that subfolder that extends the cfc at the root, then any onRequestStart() function I place there will overwrite the parent's onRequestStart().

Right now, I'm figuring the only way to make the child's onRequestStart() "the same as the parent's, with just a little extra" is copy the parent cfc's onRequestStart() to the child, and add my extra security code inside it. However, doing this will screw up the function that finds a page's relative webroot path. For pages in that subfolder, the "../../" will be relative to application.cfc in the subfolder, not relative to the parent at the root, which is what's desired.

My hacky way of working around this has been to put code in the parent cfc's onRequestStart():

<cfif cgi.script_name CONTAINS "/mySubFolder/">
   (Test the logged-in user's session.roleId; if test fails, cflocation to page that reads "you don't have permissions to view")
</cfif>

And basically do the same for any other subfolders or subsubfolders where I want additional code to run for that subfolder and any of its descendant folders. Which is okay on this particular site as it's not that large, but I see how this could become very messy for a larger site. Is there a better way to do this?

Upvotes: 4

Views: 1036

Answers (2)

Cmaso
Cmaso

Reputation: 1113

Yeah, I Googled on variations of "application.cfc", "child", "extends", and "subfolder", and a few of Ben's blog posts were at the top of the results, but not that one.

One thing that should be added for anyone else looking for the same answer -- Ben explains elsewhere that when adding the "extends" attribute for the cfcomponent tag of your child cfc, CF won't let you simply use "extends='Application'", because it first looks in the child's own folder for a file named "application.cfc", finds it, and throws an error saying a cfc can't extend itself.

Getting around this is very simple -- Ben goes on to explain that you just need to create a cfc at the root, with a name other than "application.cfc", that extends application.cfc at the root. It's essentially empty, with none of its own functions or variables. So you create a cfc named "applicationProxy.cfc" (or anything you like) at the root, then put the following code in it:

<cfcomponent displayName="applicationProxy" extends="Application"></cfcomponent>

then in your child application.cfc, you simply use:

<cfcomponent extends='applicationProxy'...

Upvotes: 1

Miguel-F
Miguel-F

Reputation: 13548

Your Google-foo must have been failing you ;) Ben Nadel has an example of what you are trying to do. Basically, you don't need to copy the function from your inherited Application.cfc, you just need to call it (invoke it) using the SUPER scope.

Here is a link to Ben's article - Ask Ben: Extending Application.cfc And OnRequestStart() With SUPER. From that article:

I have one word for you: SUPER. The SUPER scope is created when one ColdFusion component extends another one. It is available to the extending component and gives access from the extending component up into the extended component, sometimes referred to as the "base component" or "super component." The SUPER scope not only allows us to get access to values in the super component (which isn't necessary since those values are also available in the extending component), but more importantly, it allows us to execute functions in the super component even when those functions are being overridden by the extending ColdFusion component (as they will be in our demo).

To leverage this and pull in the configuration data that you have in your root Application.cfc ColdFusion component, we are going to use the SUPER scope to invoke the OnRequestStart() event method of the root Application.cfc from the sub Application.cfc. But first, let's take a quick look at the root Application.cfc to make sure we are all on the same page:

I don't want to copy the entire article here but he goes on to show examples with explanations of what is going on. I definitely recommend reading his entire article.

Here is his example Application.cfc using the SUPER scope:

<cfcomponent
    output="false"
    extends="personal.ben......app_extend2.Application"
    hint="Secondary application event handler.">

    <cffunction
    name="OnRequestStart"
    access="public"
    returntype="boolean"
    output="false"
    hint="Hanldes pre-page processing for each request.">

    <!--- Define arguments. --->
    <cfargument
        name="Page"
        type="string"
        required="true"
        hint="The template requested by the user."
        />

    <!---
        Since this application is extending the root
        Application.cfc, let's leverage the pre-processing
        power of the root OnRequestStart(). Check to see
        what value (true/false) that the root application
        would have returned for this request.
        By calling SUPER.OnRequestStart( Page ), we are
        giving the root application a chance to run logic
        on the page request. **Remember that the
        OnRequestStart() method can return false to kill the
        current page request. Therefore, we have to check to
        see what value would be returned and honor that.
    --->
    <cfif SUPER.OnRequestStart( ARGUMENTS.Page )>

        <!--- Store the sub root directory folder. --->
        <cfset REQUEST.SubDirectory = GetDirectoryFromPath(
        GetCurrentTemplatePath()
        ) />

        <!--- Return out. --->
        <cfreturn true />

    <cfelse>

        <!---
        The root application returned false for this
        page request. Therefore, we want to return
        false to honor that logic.
        --->
        <cfreturn false />

    </cfif>
    </cffunction>

</cfcomponent>

Upvotes: 3

Related Questions