Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 25, 2014 11:07
Ask Ben: Using ColdFusion Components As Return Types, Argument Types, And Property Types
<cfcomponent
output="false"
hint="I provide application settings and event handlers.">
<!--- Define the application. --->
<cfset this.name = hash( getCurrentTemplatePath() ) />
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 0, 20 ) />
<!---
Get the root directory of the application. This will
be needed to define the subsequent app-specific mappings.
--->
<cfset this.rootDirectory = getDirectoryFromPath(
getCurrentTemplatePath()
) />
<!--- Define application-specific mappings. --->
<cfset this.mappings[ "/com" ] = (this.rootDirectory & "model/" ) />
<cfset this.mappings[ "/vo" ] = (this.rootDirectory & "vo/" ) />
<!--- Define page request settings. --->
<cfsetting
showdebugoutput="false"
/>
</cfcomponent>
// Import the "vo" name space.
// NOTE: The name space that we are importing is an application-
// specific mapping and that this name-space is used to define the
// return type of one of the functions below.
import "/vo.*";
// Define the component.
component
output="false"
accessors="true"
hint="I am a contact entity."
{
// Define properties.
property
name="name"
type="string"
default=""
validate="string"
validateParams="{ minLength = 1, maxLength = 30 }"
hint="I am the full name."
;
property
name="lastValueObject"
type="vo.ValueObject"
hint="I am the last requested value object."
;
/**
* Define constructor. Notice that the return type of the
* ColdFusion component uses BOTH the app-specific mapping
* to "com" as well as the multi-part, dot-delimitted path
* to THIS component.
*
* @access public
* @output false
* @hint I return an initialized component.
**/
com.Contact function init(
string name = ""
){
// Store default properties.
this.setName( arguments.name );
// Return this object reference. Remember, when used in
// conjunction with the NEW keyword, you must explicitly
// return the object reference.
return( this );
}
/**
* Notice that in this method, we are defining a local path
* to the component (no app-specific mappings) and that the
* returnType attribute is being defined in the comments.
*
* @access public
* @returnType Contact
* @output false
* @hint I return the current contact.
**/
function getContact(){
return( this );
}
/**
* Notice that in this method, the return type "ValueObject"
* is only valid becuase our IMPORT command above imported
* the app-specific mappings / namespace, "/vo". Notice also
* that the default argument value does NOT use the imported
* namespace, but rather, refers back to the multi-part,
* dot-delimtted mapping path.
*
* @access public
* @returnType ValueObject
* @output false
* @hint I return a value-object representation of this entity. If you want to, you can pass-in an existing valueObject instance.
*/
function getValueObject(
valueObject valueObject = new vo.ValueObject()
){
// Store local properties into the given value object.
arguments.valueObject.set( "name", this.getName() );
// Store the value object as a property.
this.setLastValueObject( arguments.valueObject );
// Return populated value object.
return( arguments.valueObject );
}
}
<!---
Import the COM name space (NOTE: This actually maps to the
"model" directory thanks to our appliation-specific mappings).
--->
<cfimport path="com.*" />
<!--- Create a new Contact instance. --->
<cfset tricia = new Contact( "Tricia Smith" ) />
<!--- Rename contact (only to test accessors). --->
<cfset tricia.setName( "Tricia 'the hottie' Smith" ) />
<!---
Get the contact object (to test the return type is enforced
as a Contact instance).
--->
<cfset contact = tricia.getContact() />
<!---
Get the value object (to test that mapped-return type
based on the IMPORT command used inside the CFC).
--->
<cfset valueObject = contact.getValueObject() />
<cfoutput>
<!--- Output name contained within value object. --->
Name: #valueObject.get( "name" )#<br />
<!--- Output name contained within LAST value object. --->
Name: #tricia.getLastValueObject().get( "name" )#
</cfoutput>
<cfcomponent
output="false"
hint="I am a very simple, light-weight value object for any component - I store properties in an expandable map.">
<cffunction
name="init"
access="public"
returntype="any"
output="false"
hint="I return an initialized value object.">
<!--- Set up default properties. --->
<cfset variables.propertyMap = {} />
<!--- Return this object reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="get"
access="public"
returntype="any"
output="false"
hint="I return the given property, or property set.">
<!--- Define arguments. --->
<cfargument
name="name"
type="string"
required="false"
hint="I am the name of the property beging gotten (if omitted, fulle property set is returned)."
/>
<!--- Check to see if property name was included. --->
<cfif isNull( arguments.name )>
<!---
No property was requested; return entire
property map.
--->
<cfreturn variables.propertyMap />
<cfelse>
<!--- Return selected property. --->
<cfreturn variables.propertyMap[ arguments.name ] />
</cfif>
</cffunction>
<cffunction
name="set"
access="public"
returntype="any"
output="false"
hint="I set the given property.">
<!--- Define arguments. --->
<cfargument
name="name"
type="string"
required="true"
hint="I am the name of the property."
/>
<cfargument
name="value"
type="any"
required="true"
hint="I am the property value."
/>
<!--- Set the given property. --->
<cfset variables.propertyMap[ arguments.name ] = arguments.value />
<!--- Return this object reference for method chaining. --->
<cfreturn this />
</cffunction>
</cfcomponent>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment