Structure of CFC
A very basic CFC may be nothing more than a collection of UDFs. In some cases it may even make sense to create UDF libraries wrapped inside a CFC. But the real power of CFCs comes to play when behavior and attributes are combined in a single package. This potent combination allows CFML developers to create objects that contain both data and behavior. CFCs make it possible to create more flexible and safer applications because data are not "roaming out in the wild" to be modified by any part of the application.
The following discussion explains the structure of a basic CFC. Person.cfc will be used as an example. CFCs are regular text files (just like CFML templates are), but instead of saving the file with a .cfm or .cfml extension, the file is saved with a .cfc extension. CFCs also have a more rigid syntactical structure than CFML templates have.
The cfcomponent tag
As an introduction to CFCs, we'll be examining and building a Person object for the remainder of this primer.
All CFCs start with the cfcomponent tag and some tag attributes, all of which are optional:
<cfcomponent displayname="Person" output="false" hint="I am a Person">
Although the attributes inside the cfcomponent tag are optional, including them is highly recommended for documentation purposes. The output attribute of the cfcomponent tag simply dictates whether or not any output will be generated by the CFC. Since it has become a best practice among most CFML developers to never have CFCs generate output, it is recommended to set output to "false" because this reduces the amount of white space generated within a request.
Extends is an important attribute of the cfcomponent tag that determines how one CFC extends, or inherits from, another CFC.
The Default Constructor
In OO languages, a constructor is a method that is called (or invoked in OO terminology) when an instance (unique copy) of an object is instantiated (created). CFCs do not use traditional constructors as found in other OO languages, but CFCs do contain a "default constructor" or "pseudo-constructor," which is code that is automatically executed whenever an instance of the CFC is created.
Any code that is placed after the opening cfcomponent tag but before the first cffunction tag will be executed when the object is created. For example, if <cfset foo = "bar" /> is inserted after the cfcomponent tag, the variable "foo" would automatically be set to the value "bar" every time a new instance of the CFC is created. This is certainly handy, but it has become a best practice among CFML developers to handle constructors a bit differently by using the cffunction, cfargument, and cfreturn tags.
OpenBD will attempt to look for the init() method, which can be used as the official constructor. This method if found, will return back the instance of the object just created.
The cffunction, cfargument, and cfreturn Tags
As indicated above, the cffunction, cfargument, and cfreturn allow UDFs to be defined in a much more powerful way.
The cffunction tag, which has several attributes, acts as the opening tag of a UDF. The cfargument tag specifies what data (or arguments) are passed into the cffunction. These arguments are named, can either be required or not required, can optionally have a data type specified, and can provide a default value when the argument is not required. The cfreturn tag returns data to the caller of the cffunction; however, this is optional because not all functions will return data.
The default constructor is useful in certain situations, but it has become a best practice among CFML developers to create a cffunction in CFCs called init(), which serves as a more explicit constructor. The use of an init() function gives developers more control over the object's constructor. The opening cffunction tag for the init method, which serves as the Person CFC's constructor, looks like this:
<cffunction name="init" access="public" output="false" returntype="Person" hint="I am the Constructor">
Now let's define what makes up the Person object. In this example, firstName, lastName, and birthdate will be used as pieces of data associated with a person. When a new Person CFC is created (or instantiated), you may or may not want to set values for firstName, lastName, and birthdate. The init() method allows for the CFC to be created without passing values for the CFC's arguments because all the arguments are set as optional as opposed to required. A constructor that takes no arguments, or in which all the arguments are optional, is known as a no-arg constructor, because no arguments are required in order to create an instance of the CFC and call its constructor. Because the arguments are present, however, you may pass the arguments in when calling the constructor to set the values of the data within the CFC.
The structure of a CFC and the functions within a CFC are strict. Therefore, the cfargument tags must appear immediately inside the cffunction tag. After the arguments are declared, a setter method for each argument will be called to set the value of the attribute in the CFC to the value of the argument passed in.
Let's look at the argument tags first:
<cffunction name="init" access="public" output="false" returntype="Person" hint="I am the Constructor"> <cfargument name="firstName" type="string" required="false" default="" hint="The person's first name" /> <cfargument name="lastName" type="string" required="false" default="" hint="The person's last name" /> <cfargument name="birthdate" type="date" required="false" default="#createDate(1900, 1, 1)#" hint="The person's birthdate" />
The arguments that are passed into the cffunction are placed in their own scope, referred to by using arguments., which is then followed by the argument name. For example, to refer to the firstName argument, one would use arguments.firstName.
Next in the constructor, setter methods (explained in greater detail below) are called to set the values of the attributes within the CFC. These are not required, but this structure has become common practice for this type of object. Finally, since this is a constructor, it returns the object itself. To return the object itself, use the keyword this, which is shorthand for referring to the object itself. The cffunction is closed after the cfreturn tag, which completes the constructor For example:
<cffunction name="init" access="public" output="false" returntype="Person" hint="I am the Constructor"> <cfargument name="firstName" type="string" required="false" default="" hint="The person's first name" /> <cfargument name="lastName" type="string" required="false" default="" hint="The person's last name" /> <cfargument name="birthdate" type="date" required="false" default="#createDate(1900, 1, 1)#" hint="The person's birthdate" /> <cfset setFirstName(arguments.firstName) /> <cfset setLastName(arguments.lastName) /> <cfset setBirthdate(arguments.birthdate) /> <cfreturn this /> </cffunction>
Getters and Setters
The custom setter methods within the constructor (setFirstName(), setLastName(), and setBirthdate()) are created within the CFC in additional cffunction blocks. Getter and setter methods are common within bean-type objects, which are also known as value objects. These types of objects are typically used to represent single entities (the "nouns") within OO applications. Including getters and setters in the Person CFC allows other components within the application to access data within the Person CFC. Other components cannot access or modify data in the Person CFC without its knowledge.
Getters and setters protect the data within the CFC. Without methods that control access to data within the CFC, data could be accessed and changed easily, making the data within the CFC less safe. Inserting a function in front of getting and setting data in the CFC also allows developers to perform additional tasks, such as data type validation and security checks, before releasing or altering data within the CFC.
The following is an example of the getter and setter for the firstName attribute in the Person CFC (other getters and setters are very similar to this example):
<cffunction name="setFirstName" access="public" output="false" returntype="void" hint="I set the firstName value"> <cfargument name="firstName" type="string" required="true" hint="The new firstName value" /> <cfset variables.firstName = arguments.firstName /> </cffunction> <cffunction name="getFirstName" access="public" output="false" returntype="string" hint="I return the firstName value"> <cfreturn variables.firstName /> </cffunction>
First, take a look at the setFirstName() method. As explained above, functions may or may not return a value to the caller of the function. In cases where the function does not return anything, it is customary to specify a returntype of void, which means that the function does not return anything. Specifying a return type of void also helps to debug an application and makes things safer. If void is specified as the return type and there is a cfreturn tag within the function, an error will be thrown.
The cfargument tag has already been discussed. What may be new to you is the variables scope used in the cfset statement within the function. The variables scope (which will be discussed in greater detail below) is a scope available within the CFC itself but not directly accessible outside the CFC. This means that the methods within the CFC must be used to access the data within the CFC.
Next, look at the getFirstName() method. Note that in the cffunction tag a returntype of string is set because a value is being returned from this method (as opposed to the returntype of void in the setFirstName() method). This method is quite simple, but it protects data and allows developers to add functionality within the getter method if needed.
Remember . . . without a getter and setter method wrapped around the data, developers have less control over who accesses the data in the CFC, how other individuals access it, and what these individuals can and cannot do with it.
Putting it all together
The complete Person CFC, including all the getter and setter methods, is provided in the following example:
<cfcomponent displayname="Person" output="false" hint="I am a Person"> <cffunction name="init" access="public" output="false" returntype="Person" hint="I am the Constructor"> <cfargument name="firstName" type="string" required="false" default="" hint="The person's first name" /> <cfargument name="lastName" type="string" required="false" default="" hint="The person's last name" /> <cfargument name="birthdate" type="date" required="false" default="#createDate(1900, 1, 1)#" hint="The person's birthdate" /> <cfset setFirstName(arguments.firstName) /> <cfset setLastName(arguments.lastName) /> <cfset setBirthdate(arguments.birthdate) /> <cfreturn this /> </cffunction> <cffunction name="setFirstName" access="public" output="false" returntype="void" hint="I set the firstName value"> <cfargument name="firstName" type="string" required="true" hint="The new firstName value" /> <cfset variables.firstName = arguments.firstName /> </cffunction> <cffunction name="getFirstName" access="public" output="false" returntype="string" hint="I return the firstName value"> <cfreturn variables.firstName /> </cffunction> <cffunction name="setLastName" access="public" output="false" returntype="void" hint="I set the lastName value"> <cfargument name="lastName" type="string" required="true" hint="The new firstName value" /> <cfset variables.lastName = arguments.lastName /> </cffunction> <cffunction name="getLastName" access="public" output="false" returntype="string" hint="I return the lastName value"> <cfreturn variables.lastName /> </cffunction> <cffunction name="setBirthdate" access="public" output="false" returntype="void" hint="I set the birthdate value"> <cfargument name="birthdate" type="date" required="true" hint="The new firstName value" /> <cfset variables.birthdate = arguments.birthdate /> </cffunction> <cffunction name="getBirthdate" access="public" output="false" returntype="date" hint="I return the birthdate value"> <cfreturn variables.birthdate /> </cffunction> </cfcomponent>
© Copyright 2008 GreatBizTools, LLC All rights reserved. Republishing rights have been granted to the Open BlueDragon project by GreatBizTools, LLC.