In this chapter
When ColdFusion was introduced in the last chapter, I mentioned that one of the elements was the ColdFusion Markup Language, frequently referred to as CFML. CFML is a tag-based programming language, very similar to HTML in that the functionality that the ColdFusion server is expected to perform is indicated by a start tag like <CFOUTPUT>
and an end tag like </CFOUTPUT>
. Anything inside those tags is processed by the ColdFusion engine and everything else is left up to the web server application. This means that in the following snippet of code
<B><CFOUTPUT>Hello World!</CFOUTPUT><B>
the ColdFusion engine would output the text Hello World!
and the web server would bold the text. This might seem a little silly at this point because the web server could just as easily handle the output of the text, but as you’ll see later in the chapter, the power of ColdFusion goes way beyond creating simple text.
You’ll notice throughout this book that when I refer to tags, I type them in all caps. This is not a requirement because ColdFusion is not case sensitive (for example, <cfoutput>
is the same as <CFOUTPUT>
), but is merely a coding style that I use. By doing this, I am able to easily distinguish my CF tags from other non-ColdFusion text.
The CFML language consists of about 100 tags, each enclosed in angle brackets like HTML tags, and all the CFML tag names start with CF. Each of these tags indicates what function the ColdFusion server should perform on the content that falls between the tags.
ColdFusion also contains quite a few functions that enable you to perform operational comparisons, check for certain conditions, and even perform simple file-based operations. These functions are also invoked by a set of tags and many of them are capable of accepting arguments.
Table 18.1 lists some commonly used ColdFusion tags and functions, and later in the chapter we’ll take a look at how they operate.
Table 18.1. Some Commonly Used ColdFusion Tags
Many of ColdFusion’s functions are able to accept arguments that further refine how the function behaves. When learning the tags, pay close attention to what types of data the functions can accept.
In addition to tags and functions, ColdFusion enables you to create variables. Think of a variable as a data container that contains a unique name. Using CFML, you can not only name the container, but fill it with data as well. For instance, take a look at the following code:
<CFSET myName = "Sean">
This code defines a variable called "myName"
. It also specifies that the initial value of the variable is Sean
. Because the data being inserted into the variable is surrounded by quotes, ColdFusion recognizes it as a string—or data that can contain both numeric and alphanumeric data. After the variable is established, you can display it by using the cfoutput
tag like this:
<CFOUTPUT>#myName#</CFOUTPUT>
When calling variables, surround the variable name with pound signs so that the ColdFusion engine knows that it shouldn’t simply output the text between the tags.
When creating variables in ColdFusion, remember these tips:
Variable names must begin with a letter, underscore, or Unicode currency symbol.
You cannot begin variable names with a number.
Variable names cannot contain spaces.
Variable names are not case sensitive.
Now that you’ve created your variable, you aren’t limited to simply displaying the variable. Depending on the type of data stored in the variable, you can use it in calculations or even run comparisons against it.
When you create your variable, you also need to consider its scope. The scope of a variable defines how long the variable should exist and what portions of the page code can use the variable. Because it’s important to know when it’s okay to use your variable, it’s important to define the scope of each variable. ColdFusion has a variety of predefined scope types, so the easiest way to define the scope of a new variable is simply to assign it to one of these types.
You can specify a scope explicitly by putting it before the variable and separating the two with a period. For instance, if you wanted to define the myName
variable scope as "session"
so that it lasted for the duration of the user’s browser session, you would define it by typing
<CFSET Session.myName = "Sean">
This would define the variable, set the initial value, and define the scope of the variable as existing until the session expires.
Table 18.2 lists some commonly used variable types that are available in CFML.
Table 18.2. Commonly Used Variable Types Available in CFML
In the previous section, you saw how simple it was to call some of the built-in tags such as the CFOUTPUT and CFSET tags. Because the behaviors of these tags are predefined, adding a built-in CFML tag to your code is as easy as adding a standard HTML tag. One of the greatest features of ColdFusion, however, is its extensibility. This means that you can extend CFML beyond the capabilities of the built-in tags by creating your own custom tags, which work just like standard CFML tags. This means that you can create your own tags that contain functionality that can be reused over and over in any ColdFusion pages.
If you’re not up to authoring your own custom tags yet, literally thousands of custom tags are available through the ColdFusion Exchange at www.macromedia.com, many of them freeware or shareware. Before you go and spend a lot of time creating a custom tag, it’s usually a good idea to browse the Exchange to see whether someone else has already done the work for you. No need to reinvent the wheel!
When you create a custom CFML tag, it is stored in its own separate file. Although there are several ways of invoking a custom tag, the easiest is simply to use a tag with the name of the file, with cf_
in front of it. For example, if the custom tag is contained in a file named lastmodified.cfm
, you would invoke the custom tag by typing cf_lastmodified.cfm
. You can also invoke a custom tag with the cfmodule
and cfimport
functions. They are often favored for managing a large number of custom tags because they are capable of handling any custom tag name conflicts that might arise.
There are two kinds of custom tags: CFML and CFX. CFX tags are written in C++ or Java. Only CFML tags are covered in this book.
ColdFusion looks for custom tags first in the same directory as the calling page, then in the CFusionMXCustomTags
directory, then in subdirectories of the CFusionMXCustomTags
directory, and finally in directories that you can specify in the ColdFusion MX Administrator.
Creating your own custom tag might sound like a daunting task, but keep in mind that custom tags usually consist of code that you might be writing over and over again (hence the need for a custom tag). So if you’re going to build the same code block over and over, you might as well create a custom tag instead and save yourself some time.
The first step in creating a custom tag is to determine what it is you want to accomplish. For instance, suppose you wanted every page to display a “last updated on” text block that also included the date when the page was last updated. You could open a text editor and build the following code block:
<CFPARAM name="attributes.lastmodified" default=""> <CFOUTPUT> <I>This page was last updated on #attributes.lastmodified#</I> </CFOUTPUT>
Essentially, this code block takes a date that is passed by the calling page and displays the code block and the date. To invoke the custom tag, you would call it in a CFML page by using code similar to the following:
<HTML> <HEAD> <TITLE>My Page</TITLE> </HEAD> <BODY> Type your content here. <CFLastModified LastModified="June 6, 2005"> </BODY> </HTML>
This page now passes the date on which the page was last modified and it is then displayed, along with the text block, on the page. Although this example may seem a bit remedial, it does demonstrate that each custom tag requires two fundamental things:
The defined custom tag code
A page that calls that tag and passes any parameters to it.
Truly, the best way to fully understand how custom tags work is to build a few remedial tags yourself, and then spend time on the Macromedia Exchange downloading, reviewing, and looking at the code involved in each of the custom tags on the Exchange.
Although defining and displaying custom variables might enhance the functionality of your website, the more powerful tool that ColdFusion offers is the capability to communicate with databases and display and interact with content stored within them.
Imagine if your client, Retro’s Cycles, had 100 motorcycles in inventory for sale. It wouldn’t be reasonable to consider creating an individual web page for each of the 100 motorcycles. It would, however, be a better idea to store the information about the motorcycles in a database and then create a small set of pages that interacts with the database to display information about what is in inventory. Imagine being able to build a search page, a results page, and a details page that enable you to display the content details for each of the 100 motorcycles. That’s 3 pages that now have taken the place of 102 pages. Talk about a timesaver!
ColdFusion is able to interact with a database in several different ways. It can retrieve data, update data currently stored in the database, insert new data, or delete existing data.
The most common database interaction is simply drawing data out of the database for display on a page. To do this, however, you need to have established a connection to the database and then create a recordset. Think of a recordset as a container that holds all the database records that you have requested. For instance, if you asked the database to retrieve all records where the Make
field is equal to "Honda"
, the recordset would contain only the records for Honda bikes.
To request this data, you need to combine the <CFQUERY>
tag with a SQL query that contains the proper syntax for returning the correct records. The <CFQUERY>
tag specifies the database from which to retrieve data. The SELECT
statement specifies the table(s) within the database, and which records in the tables to get.
The simplest SELECT
statement looks like this:
SELECT * FROM Inventory
This statement says, “Retrieve all columns (fields) of all rows (records) from the Inventory table.” The asterisk is a wildcard meaning “all columns.” All rows are retrieved, because the statement contains no limiting clause for selecting only a subset of rows. In this case, each row is a different product, and the columns are the attributes of the product, such as the item’s unique ID, make, model, year, list price, and so on.
If you want to limit the records your recordset contains, you can use a WHERE
clause to select only specified rows. For instance, the following statement retrieves only records where the Make is equal to "Honda"
:
SELECT * FROM Inventory WHERE Make = 'Honda'
To use this SQL statement in ColdFusion, enclose it in a <CFQUERY>
tag. In the following example, the <CFQUERY>
tag specifies the retroscycle database, and assigns the name rsHondas
to the recordset that is created:
<CFQUERY name="rsHondas" datasource="retroscycles"> SELECT * FROM Inventory WHERE Make = 'Honda' </CFQUERY>
Adding content to your database is almost as simple as retrieving data. Inserting a record usually requires completion of a form and form fields: You enter the data into the form and have it added after you submit it. The form then passes the data to a page (or code on the same page) that processes a SQL command similar to this:
INSERT INTO Inventory (Year, Make, Model) VALUES (#FORM.Year#, #FORM.Make#, #FORM.Model#)
Basically, this SQL query takes the contents of the Year, Make, and Model form fields and adds them to the Year, Make, and Model fields in a new record in the database.
Therefore, when you create your full CFML code, it would look something like this:
<CFQUERY name="rsHondas" datasource="retroscycles"> INSERT INTO Inventory (Year, Make, Model) VALUES (#FORM.Year#, #FORM.Make#, #FORM.Model#) </CFQUERY>
Another common database interaction is updating a record that already exists in the database. Like the Insert process, this requires two different elements: a form that displays the existing data and allows you to change it, and an action that replaces the existing data with the updated information after the form is submitted.
The form is created with a standard HTML form, or you can use a form created in CMFL by using the <CFFORM>
tag. The update action uses the <CFQUERY>
tag, along with the SQL update statement that looks something like this:
UPDATE Inventory SET Make='Kawasaki' WHERE Make='Honda'
This update statement modifies all records in the database where the Make
is currently Honda and changes the Make field to Kawasaki. Although this is a global change to the entire table, you can limit the update to a single record by further refining the WHERE
clause to look something like this:
UPDATE Inventory SET Make='Kawasaki' WHERE Make='Honda' AND InventoryID = '00011'
This update statement would affect only the record that has an InventoryID
of 00011.
Because it’s likely you’ll be making the updates via a form, the form variables that are submitted can also be used in the SQL statement. For example, look at the following:
UPDATE Inventory SET Make=#FORM.Make# WHERE Make='Honda' AND InventoryID = #FORM.InventoryID#
This grabs whatever value is stored in the Make form field and enters it into the Make field in the database. Note that this affects only the record that is passed in the InventoryID
form field.
Although deleting records is certainly an important part of database maintenance, it is important to remember that deleting permanently and irrevocably removes records from the database. There is no Edit, Undo command, and you can’t restore the data without resorting to a known, good backup such as a tape backup or SQL backup file. So use the command deletion process wisely and cautiously.
When deleting records, the more detail about which record to delete, the better. For instance, consider this simple statement:
DELETE * FROM Inventory
Because it puts no limits on what to delete, it totally wipes out all the records in the Inventory database. Gone...wiped...kaput.
Therefore, it’s a wise idea to restrict your delete commands so that they affect only the record you want. For instance, consider the following command:
DELETE FROM Inventory WHERE InventoryID = '00001'
If the InventoryID
field is a primary key that increments automatically, you can be sure that there will only ever be one record with an InventoryID
of 000001. This means that you have deleted only one record and you hope it’s the correct one.
Keep in mind that relationships between tables can cause delete operations to fail. For example, if you had a Sold
table that depended on the Inventory
table and you tried to execute a DELETE FROM Sold
(delete all records in the Sold
table), you would receive an error stating The record cannot be deleted or changed because table 'Inventory' includes related records
.
I tried connecting to my database, but I got an error that connection could not be established. What happened?
There are a couple things you can check. First, ensure that you spelled the data source name correctly in your code. If it is correct, check that the path specified in the ColdFusion Administrator is still the correct path to the database.
I am receiving a syntax error that I just can’t seem to figure out. Where should I look to resolve the issue?
First off, pay close attention to the line number that is being indicated in the error. Look at that line and determine whether you forgot to add an end tag or close a quote or add a pound sign at the beginning or end of your variables. These three things cause a lot of syntax errors and are usually easy to track down after you know what to look for.
As you begin developing CFML code, I can’t stress to you how important it is to document your code as you go along. Just like HTML, CFML provides special character combinations that indicate when a comment tag begins and when it ends. To start a comment, the <!- combination is used, whereas the --> combination is used to indicate that a comment has ended. For instance, suppose you write a block of code that draws two values from the database and averages them. Something as simple as adding a comment line such as the following
<!--This function draws the number of motorcycles in the database and the prices of all the motorcycles and creates an average price-->
can save you a lot of time when you have to revisit your code down the road. In addition, if someone else comes along and has to interpret your code, these helpers go a long way in helping them to understand what you were trying to accomplish.
Start developing your documentation skills now and you will reap the benefits of clear, understandable code for the rest of your career.
18.227.26.217