Using the pseudo-constructor is not the most secure way of working with components, but it has its uses. A constructor’s purpose is to execute when an object is created (instantiated), and this is usually done inside a function/method in other object-oriented programming languages. The pseudo-constructor on the other hand was executed outside of any functions (but before the first function).
In CFML, we name our constructor “init”, which stands for initialize. But before we get into using the init() constructor, let’s do one more example using a pseudo-constructor, and then replace it with the init() constructor.
<cfcomponent displayname="city" output="false">
<!--- Private Variable (pseudo-constructor) --->
<cfset variables.cityName = "" />
<!--- Setter Method --->
<cffunction name="setName" output="false">
<cfargument name="newName" type="string" />
<cfset variables.cityName = arguments.newName />
</cffunction>
<!--- Getter Method --->
<cffunction name="getName" output="false">
<cfreturn variables.cityName />
</cffunction>
</cfcomponent>
In this component we have a variable called “cityName” that is private. Each time we create an object of this component, this variable is initialized since it is before the first method (function). We then have two methods, one that sets the name of the variable through an argument it takes, and other returns (gets) the variable. These are called setter/getter methods, they are also called “mutators” in other object-oriented programming languages. The point of them are to set (modify) and get (return) data in an easier way so that when applications get bigger, additional functionality can be added more easily.
In our calling page, we could have this:
<cfset cityObject = CreateObject("component","com.city") />
<cfoutput>
#cityObject.setName("New York City")#
#cityObject.getName()#
</cfoutput>
Here we created an object called “cityObject”. And we accessed the “setName” method and passed an argument to it. To return the modified data we used our “getName” method.
Using the Init() Constructor
In the changed example below, we created a function and named it init (which is going to act as our constructor). Our function is going to take an argument named “cityName” and set a variable called cityName to that argument (which is also called cityName). All of this in then returned in the this scope. When using the init() method it is required that everything been returned in the this scope otherwise the methods in our component (setter/getter) will not be accessible.
<cfcomponent displayname="city" output="false">
<!--- Constructor --->
<cffunction name="init" output="false">
<cfargument name="cityName" type="string" />
<cfset variables.cityName = arguments.cityName />
<cfreturn this />
</cffunction>
<!--- Setter Method --->
<cffunction name="setName" output="false">
<cfargument name="newName" type="string" />
<cfset variables.cityName = arguments.newName />
</cffunction>
<!--- Getter Method --->
<cffunction name="getName" output="false">
<cfreturn variables.cityName />
</cffunction>
</cfcomponent>
And the calling page:
<cfset cityObject = CreateObject("component","com.city").init("Chicago") />
<cfoutput>
#cityObject.getName()# <br />
#cityObject.setName("New York City")#
#cityObject.getName()#
</cfoutput>
One thing you will notice here, is that when we created our object with the CreateObject() function, we also attached the init() method to it (with an argument as well). This is common practice with the init method and is a way to make sure it is initialized upon object creation. We also changed our cityName variable and gave it a new name via the setter method between our <cfoutput> tags.
Now let’s modify our city.cfc component and make it even better:
<cfcomponent displayname="city" output="false">
<cffunction name="init" output="false">
<cfargument name="cityName" type="string" />
<cfargument name="country" type="string" />
<cfargument name="continent" type="string" />
<cfset setCityName (arguments.cityName) />
<cfset setCountry (arguments.country) />
<cfset setContinent (arguments.continent) />
<cfreturn this />
</cffunction>
<cffunction name="setCityName" output="false" returntype="void">
<cfargument name="cityName" type="string" hint="New cityName value." />
<cfset variables.cityName = arguments.cityName />
</cffunction>
<cffunction name="getCityName" output="false" returntype="string" hint="Returns cityName value.">
<cfreturn variables.cityName />
</cffunction>
<cffunction name="setCountry" output="false" returntype="void">
<cfargument name="country" type="string" hint="New country value." />
<cfset variables.country = arguments.country />
</cffunction>
<cffunction name="getCountry" output="false" returntype="string" hint="Returns country value.">
<cfreturn variables.country />
</cffunction>
<cffunction name="setContinent" output="false" returntype="void">
<cfargument name="continent" type="string" hint="New continent value." />
<cfset variables.continent = arguments.continent />
</cffunction>
<cffunction name="getContinent" output="false" returntype="string" hint="Returns continent value.">
<cfreturn variables.continent />
</cffunction>
</cfcomponent>
Here we added three arguments that our init() constructor takes, the cityName, country, and continent. One thing that you will notice that is new in this example, is that we <cfset> the setter methods in our component with the arguments that are initialized when our object is created. In our methods we also added the hint attribute, giving a short sentence on what each method (or argument) does. We also put the returntype, which we set to either string (since we are returning a string) or void (not returning anything).
And here is our template that calls for the object creation:
<cfset cityObject = CreateObject("component","com.city").init("St. Louis", "United States", "North America") />
<cfoutput>
#cityObject.getCityName()#,
#cityObject.getCountry()#,
#cityObject.getContinent()#
</cfoutput>
Variable Scopes in Components
The three variable scopes inside components are: this, variables, and var. The this scope allows access to a variable inside the component and also outside of the component. The variables scope privatizes the variable so that it is only accessible inside the component (all the methods can use it). The var scope is only accessible inside a method/function and can not be referenced outside of that method/function. It’s usually good practice to make variables and functions private that our not directly being referenced by the calling template.