CFWheels

Defining functions in cfwheels

Recently, when working with a developer that was new to cfwheels, I was asked, "Where is the best place to put functions that manipulate data?" Like a good consultant I answered the question with a question - "What do you mean by 'manipulate data'," I asked. Really the answer lies somewhere in the "classic" separation of concerns which in MVC architecture is pretty clear - are we talking manipulation for visual reasons, business logic reasons or persistence? Or perhaps, multiple reasons? there's your answer.

So, using an example, lets say we are working with an employee record (table-name: employee) (yes, I break the convention and  use singluar table names cfwheels people, bite me, it's my app, i like it more. A nice feature of wheels is that you can break conventions and configure when you want or need to.) Anyway, Let's assume we need to manipulate some data. where do we put the function?

Example 1: encrypt the data for a field on the way into or out of the database table

Solution 1: put it in the model. the concern is persistence. Unless you are going to be displaying or otherwise "controlling" the encrypted values somewhere else in the app (not likely) then the other concerns of our architecture dont need to know about this functionality - so don't let them even see it: Thus, in /models/employee.cfc add

<cffunction name="encryptPhone" returntype="void" hint="I'll encrypt the phone field">
	<cfset this.phone = encrypt(this.phone,"somekey") />
</cffunction>

Note: If  this is a generic function that will be needed in many models (or can be used in many models, say, something like a function to generate an audit trail record), then add this code to the ancestor class /models/model.cfc. Then, all of your models will already have this functionality wired in AND it will still be isolated to our persistence concern

Example 2: manipulate form data for validation or other conversion of request parameters to something structurmafied (yes, that's my own word)

Solution 2: put it in the controller

Singletons Business Objects in CFWheels

Like many application development frameworks, there is lots of plumbing within that you don't have to use, but hey, it makes a ton of sense. Singleton object management is a good use case. Now let's be clear, I could add something like ColdSpring to offload my object factory pattern to a separate  framework - that often makes sense, but depending on the client and how encapsulated you want to keep things, sometimes just a good clean way to do it on your own makes the most sense.

So we want to make instances of a component and keep these instances around beyond a request. In CFWheels, what's the best way?

As it turns out, the answer is mind-bogglingly simple - but we have to consider (2) things:

  1. Where do we create the object instances?
  2. How will we be able to reference them?

In straight-up ColdFusion (no framework), we have Application events like onRequestStart, onSessionStart, OnApplicationStart & onServerStart that answer question#1. As for how to reference them, well, that's easy too - we can just use the "scope" of choice for the persistence - "request, session,application or server" and your done. Of course you may need a trigger to "refresh" the cached instances (now you're adding some plumbing). Here's the "old school" way of handling this (or at least the non-framework way)

<cffunction name="onApplicationStart" returntype="boolean" output="false">
	<cfset buildCache() />
	<cfreturn true />
</cffunction>

<cffunction name="onRequestStart" returntype="boolean" output="false">
	<cfif structkeyExists(url,"reload")>
		<cfset onApplicationStart() />
	</cfif>
	<cfreturn true />
</cffunction>

<cffunction name="buildcache" hint="I'll build the app cache of singletons">
	<cflock scope="application" type="exclusive" timeout="10">
		<cfset util = createObject("component","lib.util.widget").init() />
	</cflock>
</cffunction>

But with CFWheels, we really should leverage the framework and not worry about micro-managing these events (although we certainly can if we want to get that granular) - so check it out. Answer #1: config/settings.cfm Answer#2: use the CFWheels set() function:

<cfscript>
	set(util= createobject("component","lib.util.widget").init());
</cfscript>

The Beauty of this solution is that settings.cfm only fires when the application loads (or reloads) - essentially (but not identical to) the onApplicationStart event. Secondly, to provide visibility of our variable anywhere in the application, using the set() function allows us to access the variable by using the get() function

<cfscript>
	util = get("util");
</cfscript>

please note: This solution is specifically for application singletons - behind the scenes, CFWheels is using the application scope for these settings - in fact, the documentation on config/settings.cfm explicitly states this file is for configuration - which is in fact a good use case for application level persistence, but not the only use case. Singleton (Business) Objects are also perfectly viable candidates for this same sort of persistence.

 

$createNestedParamStruct is a cfwheels badass

One of the nice things about the cfwheels framework is the ability to build forms that map directly to a model object using form helpers. On the "receiving" end (in your controller), you have access to a key in the params scope that is a nested structure of data that'll line right up with your model. This stuff makes scaffolding CRUD forms a serious layup.

#select(label="org", objectName="contact", property="org_id",options=orgs, valueField="org_id", textField="org_name",includeBlank="Select an Org")#

Recently, I have been working on a standard set of scaffolding templates to take the CRUD generation to a whole new level. Along with a few methods in the base controller (and some helper methods for the view) I've worked in generic code to include pagination, column header sorting and filtering in all the generated templates. While I was working on the filter controls, I realized I'd seriously like to have a nested param structure with all the filter fields contained within. But the form helpers are specifically for mapping fields to a model object, and I'm not doing that. So I looked further into how wheels constructs those nested params for the form helpers and low-and-behold, i found a simple little function that when used with the right naming convention, tears it up on the form-field parsing front. And, it's called on every request automagically, so you aint gotta do nuthin to make it work. Schvinnnng!

Looking inside /wheels/dispatch/request.cfm i found the $createNestedParamStruct() method. After discovering how it expects and parses field names, i then reverted back to my filter form and started naming fields like this:

#selectTag(label="type",name="filters[label_id]", options=labels,textfield="label_description",valueField="label_id")# #selectTag(label="org",name="filters[org_id]", options=orgs,textfield="org_name",valueField="org_id")# #selectTag(label="contact",name="filters[contact_id]", options=contacts,textfield="full_name",valueField="contact_id")#

Notice I'm not using the objectName or property arguments to the selectTag function (like we did in the select(0 function for the model object mapping) - selectTag doesn't tie the form field to a model "object" But, by using the [] notation on the field name, wheels automatically creates a sub-structure in the params scope when the post occurs... so by the time I can even get my hands on these any of these params in the controller, it's already neatly encapsulated in a sub-structure named "params.filters"

CFWheels Workarounds Numero Uno - Application Proxy

One of the interesting features of the CFWheels framework is the fact that the core application architecture leverages <cfinclude /> tags.  I say interesting because in an environment that is essentially java under the hood, most frameworks use inheritance. Such is the case with FW/1 ("Framework One"), the awesomely simple to use framework from Sean Corield

FW/1 implementation of Application.cfc

<cfcomponent extends="org.corfield.framework">
</cfcomponent>

CFWheels implementation of Application.cfc

<cfcomponent output="false">
   <cfinclude template="wheels/functions.cfm">
</cfcomponent>

In many ways, how it is wired in doesnt really matter... and <cfinclude /> is lighting fast, much faster than inheritance. The challenging part becomes when you want to override (or extend) a method in the core application framework - you can't just create the method and use super.method() as needed. Instead, wheels creates a way for you to piggyback onto the application events, but at that point you are at the mercy of the framework and whatever code it already ran - there is no obvious way to orchestrate the firing order. your custom code comes last, done deal.

But of course where there is a will, there is a way. while it would be a big no-no to hack the framework code itself (in the wheels directory) the answer is quite simple. simply rename your root Application.cfc file (coded above) and name it "Wheels-Application-Proxy.cfc" Then, just create the following Application.cfc in your root directory

<cfcomponent extends="Wheels-Application-Proxy">
</cfcomponent>

Now if you want to rewrite the OnRequestEnd event (say to modify that ugly debug information wheels gives you, or perhaps tweek it a bit) you are ready to go.  In such a case you may want to be careful... if you aren't going to call super.OnRequestEnd() at some point in your method, you may want to have a peak in the framework code and see what its doing and be sure you dont cut out any fundamental framework code... but that's easy enough to do with a copy-and-paste