Aliaspooryorik
ColdFusion ORM Book

Accessing the session scope from CFCs

I've just discovered a great post by Peter Bell about the issue of accessing the session scope in OO applications. This post doesn't add anything new (apart from some code samples) but it just clicked with me so thought I'd share.

Basically there are three options when dealing with session and cfcs (Please note this code is trimmed down for ease of reading - don't forget to add output="false" to your cfcomponent and cffunction tags).

1) Just reference the session scope in your CFCs


<cfcomponent name="Authentication">
<cffunction name="IsUserLoggedIn" returntype="boolean">
<cfreturn StructKeyExists( session, "userid" ) />
</cffunction>
</cfcomponent>

This works, but it does mean that we are accessing a scope that isn't injected into our object. Not a huge problem unless you have more than one cfc referencing the session scope and then decide you want to store the user in as a cookie. That's just going to be end up with a big mess.

2) Create a cfc which handles setting and getting session values for your whole application.


<cfcomponent name="SessionFacade">
<cffunction name="setKey" returntype="void">
<cfargument name="key" />
<cfargument name="value" />
<cfset session[ arguments.key ] = arguments.value />
</cffunction>
<cffunction name="getKey" returntype="any">
<cfargument name="key" />
<cfreturn session[ arguments.key ] />
</cffunction>
<cffunction name="IsKeyValid" returntype="boolean">
<cfargument name="key" />
<cfreturn StructKeyExists( session, arguments.key ) />
</cffunction>
</cfcomponent>

<cfcomponent name="Authentication">
<cffunction name="IsUserLoggedIn" returntype="boolean">
<cfset var SessionFacade = CreateObject('component', 'SessionFacade').init() />
<cfreturn SessionFacade.IsKeyValid( "userid" ) />
</cffunction>
</cfcomponent>

Hurrah - problem solved, we've moved the responsibilty of getting and setting the session variables to our new class. Not so fast! What happens if we decide to store the userid in the cookie scope, we now need to go off and create a CookieFacade. The Authentication class still needs to know the scope (session or cookie) so that it can call the correct class (SessionFacade or CookieFacade) we really haven't gained anything.

3) Create a scope agnostic class.


<cfcomponent name="SiteUser">
<cffunction name="setUserId" returntype="void">
<cfargument name="value" />
<cfset session[ "userid" ] = arguments.value />
</cffunction>
<cffunction name="getUserId" returntype="numeric">
<cfreturn session[ "userid" ] />
</cffunction>
<cffunction name="IsUserAutheticated" returntype="boolean">
<cfreturn StructKeyExists( session, "userid" ) />
</cffunction>
</cfcomponent>

<cfcomponent name="Authentication">
<cffunction name="IsUserLoggedIn" returntype="boolean">
<cfset var SiteUser = CreateObject('component', 'SiteUser').init() />
<cfreturn SiteUser.IsUserAutheticated( ) />
</cffunction>
</cfcomponent>

It's a subtle change, but now we could easily change our SiteUser class to use cookies and our Authentication class need never know.

As an additional note, as Sean Corfield points out in the comments of Peter's post, you could still have a SessionFacade and a CookieFacade which SiteUser uses.

I'd also like to thank Bob Silverberg, Adrian Moreno and Brian Kotek for pointing me in the right direction (hopefully I've got it right!).


13 comments

  1. I would not even limit your post to the sesssion scope. Your components should never understand anything about scope. This is also helps to make your component reusable when its not tied to a particular scope.

    Comment by Dan Vega – January 23, 2009
  2. Hi Dan, Yeah that's what I was hinting at with the SiteUser using SessionFacade and CookieFacade.

    Comment by John Whish – January 23, 2009
  3. John, I think there might be a typo #3 for your SiteUser class - you have the function IsKeyValid() defined twice while missing the setKey() function. Thought you might want to know.

    Comment by Bim Paras – January 23, 2009
  4. In your scope agnostic code example the isKeyValue cffunction is shown twice and you don't have a setkey function.

    Comment by Michael Brennan-White – January 23, 2009
  5. @Bim and @Michael thanks for spotting my deliberate mistake - I've updated the post :)

    Comment by John Whish – January 23, 2009
  6. There is one version of the SessionFacade that you missed, which is one that encapsulates access to the shared scope without using generic getters and setters.

    I often use a SessionFacade, but my usage of it appears to be quite different from what most people talk about when this topic comes up. First, I don't use a generic object with a generic getter and setter than anything can use to dump anything into the shared scope. I limit what is available to only what is needed, i.e. getUser, getShoppingCart, etc. So in that way it does seem much closer to your SiteUser.

    Second, everyone assumes that calling it a "session facade" is somehow bad because it binds it's purpose to ColdFusion's session scope. I don't see it this way. ColdFusion happens to have a scope called "session" that is one way to persist information across HTTP requests. But it is not the only way, cookie and client also come to mind. When I call this object a SessionFacade, I don't mean it as "facade to the session scope", but rather as "facade to whatever handles dealing with data that exists across multiple HTTP reqeusts, which is typically called an HTTP session".

    I realize that I'm probably in the minority on this, and that the naming is can be seen as ambiguous, but I still find that using it allows for much easier unit testing since it becomes simple to mock the object and allow tests to run without having to create an entire application/session/client/cookie setup around it. Add to that the fact that I typically am dealing with a very limited set of data in the session scope, which is only referenced from an equally limited number of service object methods, and the end result is that, for me, the pros outweigh the cons.

    That's my story and I'm sticking to it! ;-)

    Comment by Brian Kotek – January 23, 2009
  7. I'm curious about the lack of using the var keyword. Also, why no locking when writing to the session scope? Grax!

    Comment by Allen – January 25, 2009
  8. @Brian, I agree, that a SessionFacade without generic getters and setters would be better. The concept that I was trying to convey was that the class shouldn't be tied to a specific scope.

    I know exactly what you mean about naming conventions! I wanted to call it Session (as in user session), then it was BrowserSession (as session/cookies are browser dependant). Then it became Request, I've finally ended up calling it ScopeFacade (although I'm not convinced it should even have 'Facade' in the name!). Although reading your comment again "Shared Scope" pretty much describes what the class is for, so maybe I should use that! :)

    @Allen, yep, I should have a var keyword before "SiteUser" in my class. I'll update the post. I just typed the code directly into my blog - I really must check it! As for locking, I didn't foresee any issues with race conditions in this particular case. There is an excellent post here on cflock:
    www.coldfusionguy.com/ColdFusion/blog/index.cfm/2008/1/11/Understanding-CFLock

    Comment by John Whish – January 26, 2009
  9. Thanks!

    Comment by Allen – January 27, 2009
  10. Great post John! There is one thing that's bothering me. As is my understanding of CF, to be able to work with Session scope, you need to be "under" Application.cfc/cfm. What if you have many applications reusing common shared components? Can SiteUser.cfc be located among them? Let's imagine such situation (spaces substituted by "-"):

    /sharedCFCs
    ---/sessionMamagement
    --------SiteUser.cfc
    ---/otherCFCs
    --------someCFCCallingSiteUser.cfc

    /www1
    ----Application.cfc
    ----page1CallingSiteUser.cfm

    /www2
    ----Application.cfc
    ----page2CallingSiteUser.cfm

    As you can appreciate, there is no Application.cfc/cfm in "/sharedCFCs" dir. I am wondering if such code will treat sessions well. Will someCFCCallingSiteUser.cfc get the right session if invocated by some page from www1 dir? Will page1CallingSiteUser.cfm and page2CallingSiteUser.cfm work properly? Or I just get the whole thing wrong? Hope to hear your oppinion.

    Comment by Jack – March 10, 2009
  11. @Jack, you're right that the session scope only exists within the application namespace. If you're running them all on the same domain or subdomain then use a cookie to store the user's unique identifier and then keep the values in a database (or cookie if not too long or personal).

    Does that help?

    Comment by John Whish – March 10, 2009
  12. @John, thank you for your answer.
    That means scope-agnostic SiteUser.cfc has to be located within the scope-aware application namespace to work properly. If located anywhere else - the logic of maintaining persistent data has to be programmed and maintained repeatedly within the code located under Application.cfc/cfm.
    If I am correct, there is a lot of code rewriting waiting for me :) Thanks again for your comment!

    Comment by Jack – March 10, 2009
  13. @Jack - hopefully you've encapsulated the access to your session scope and so switching it to use cookies shouldn't be too much work :)

    Comment by John Whish – March 11, 2009

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.

Please subscribe me to any further comments
 

Search

Wish List

Found something helpful & want to say ’thanks‘? Then visit my Amazon Wish List :)

Categories

Recent Posts