Matt Gifford aka coldfumonkeh | Consultant Developer
View Github Profile


Understanding ColdFusion 10 Method Chaining

Jun 27, 2012

One of the many new enhancements to the ColdFusion 10 language has been the ability to chain methods in your ColdFusion components.

What's chaining?

If you're a jQuery user, chances are you've seen (and probably used) method chaining before. It's a simple (but powerful) way to group your method calls together to perform actions on the same object / element:

$('#myElement')
    .css('backgroundColor','yellow')
    .attr('title', 'Chaining methods... heck yeah!');

In Action

Let's take a look at how you can use method chaining in ColdFusion 10.

We'll start off with a simple Person bean / entity object. In this object we want to store the first name, last name and email address for our individual.

<cfcomponent output="false" accessors="true">

	<cfproperty name="firstname" type="string" default="" />
	<cfproperty name="lastname" 	type="string" default="" />
	<cfproperty name="email" 	type="string" default="" />

	<cffunction name="init">
		<cfreturn this />
	</cffunction>

</cfcomponent>

There are a number of ways that you can instantiate a component, and depending on whether you have developed the object to be 'read-only' (you can only set the properties from the init / constructor method within the object, not from any calling pages or templates) or not, you would have to set each property one at a time, like so:

<cfset objPerson = new person() />
<cfset objPerson.setFirstname('Matt') />
<cfset objPerson.setLastname('Gifford') />
<cfset objPerson.setEmail('[email protected]') />

ColdFusion 10 method chaining now means you can set any of your object's properties in one go, like so:

<cfset objPerson =
			new person()
			.setFirstname('Matt')
			.setLastname('Gifford')
			.setEmail('[email protected]')
		/>

The Rules

There are some rules in place which define how far you can go when chaining your methods:

  • The accessors attribute is set to true
  • If the setter functions for the property are defined
  • Until a method is found

Let's have a look at these to try and better understand them.

Accessors attribute set to true

You can see from the previous code sample showing the person entity that the opening cfcomponent tag has the accessors attribute set to true. Without this, ColdFusion will not generate the implicit accessors and mutators (or getters and setters) for each property.

Let's see what happens when you have it set to false or not defined and have chosen to write your own getter and setter methods. Here is an amended version of our person object:

<cfcomponent output="false">

	<cfproperty name="firstname" type="string" default="" />
	<cfproperty name="lastname" 	type="string" default="" />
	<cfproperty name="email" 	type="string" default="" />

	<cffunction name="init">
		<cfreturn this />
	</cffunction>

	<cffunction name="setFirstname">
		<cfargument name="firstname" />
		<cfset variables.firstname = arguments.firstname />
	</cffunction>

	<cffunction name="setLastname">
		<cfargument name="lastname" />
		<cfset variables.lastname = arguments.lastname />
	</cffunction>

	<cffunction name="setEmail">
		<cfargument name="email" />
		<cfset variables.email = arguments.email />
	</cffunction>

</cfcomponent>

Running this using the chaining code above will not end well:

ColdFusion 10 method chaining without implicit accessors

ColdFusion will assume that the second method, setLastName(), is contained within the setFirstName() method call, in the same way a public function would be available within an instantiated component. Without the magic of implicit accessors, chaining will not work.

Setter functions are not defined

In this example, the setter attribute for the email property has been set to false. Instead of using the implicit mutator we're going to create our own setEmail() method in the CFC just to see what happens if we try to call it as part of the chaining.

Here's our revised CFC:

<cfcomponent output="false" accessors="true">

	<cfproperty name="firstname" type="string" default="" />
	<cfproperty name="lastname" 	type="string" default="" />
	<cfproperty name="email" 	type="string" default="" setter="false" />

	<cffunction name="init">
		<cfreturn this />
	</cffunction>

	<cffunction name="setEmail">
		<cfargument name="email" />
		<cfset variables.email = arguments.email />
	</cffunction>

</cfcomponent>

And here we are setting the property values once more:

<cfset objPerson =
			new person()
			.setFirstname('Matt')
			.setLastname('Gifford')
			.setEmail('[email protected]')
		/>

If we run the above code against the revised person object, the mutators will successfully set the values for the firstname and lastname properties but will error when trying to set the email, even though we defined our own setEmail method:

ColdFusion 10 method chaining needs setter functions defined

We could get around this by calling our defined setEmail method after the initial chaining, like so:

<cfset objPerson =
			new person()
			.setFirstname('Matt')
			.setLastname('Gifford')
		/>

<cfset objPerson.setEmail('[email protected]') />

but this then bypasses the awesomeness provided by chaining your methods and the implicit getters and setters. Why create more work for yourself if you don't have to? Of course, there may be instances when you want or need to run setter functions this way... it's always good to know you have options.

Until a method is found

Method chaining will work for any valid (for valid, see points one and two above) properties within your component and will happily set the values for as many as you wish UNTIL it reaches a method call, at which point it will stop setting values.

Here is another revised version of the person object, which now contains a getFullName() method to return a concatenated string containing the firstname and lastname property values:

<cfcomponent output="false" accessors="true">

	<cfproperty name="firstname" type="string" default="" />
	<cfproperty name="lastname" 	type="string" default="" />
	<cfproperty name="email" 	type="string" default="" />

	<cffunction name="init">
		<cfreturn this />
	</cffunction>

	<cffunction name="getFullName">
		<cfreturn getfirstname() & ' ' & getLastname() />
	</cffunction>

</cfcomponent>

Running the following code will work as all property values will be set before returning the value from the getFullName() method:

<cfset objPerson =
			new person()
			.setFirstname('Matt')
			.setLastname('Gifford')
			.setEmail('[email protected]')
			.getFullName() />

<cfdump var="#objPerson#">

Running the following will NOT work as ColdFusion will not be able to find the setEmail() method within the object:

<cfset objPerson =
			new person()
			.setFirstname('Matt')
			.setLastname('Gifford')
			.getFullName()
			.setEmail('[email protected]') />

<cfdump var="#objPerson#">

ColdFusion 10 method chaining will continue until it finds a method

Don't Go Nuts

Method chaining is awesome and can really help streamline your code, but if you have a multitude / butt-load of properties within the object, try not to go crazy and set all of the values in one long chain. It IS possible to do that, but it will start to make the code unreadable and potentially unmanageable.

Remember that code is a language and languages need to be read.

The End

So there you go... method chaining in ColdFusion 10 is an amazing addition to the language and one that can really help you minimise code, increase productivity and generally make your day that little bit happier.

 


Latest Blog Posts

Jul 16, 2020
Github Actions with CommandBox and TestBox
Read More
Jul 9, 2020
Azure pipelines with CommandBox and TestBox
Read More
Dec 23, 2019
CFML content moderation detection component library
Read More