As described in Lesson 1, it is important to define the right data type for each column on the tables in the database. A fundamental concern, therefore, is ensuring that the captured data is suitable for the column definitions in the Trips table. This type of validation on a single field is often referred to as a single-field edit.
Compass Travel has other operating policies that involve evaluating the values from more than one field. These validations, referred to as cross-field edits, are usually more difficult to program. To assure that new trips are uniformly captured, Compass Travel has published cross-field validations and single-field edits in its Compass Travel business rules.
The following table lists the Compass Travel business rules for capturing and editing trip information. This table identifies which rules requiring single or cross-field editing.
ColdFusion provides special tags to simply the process of enforcing business rules. Using ColdFusion, it is possible to enforce business rules in several places. For example, you can enforce some validation edits on the client. Other validation edits, you can enforce on the server after the data entry form is submitted. You will explore these options in the following sections.
The first approach you will take to enforce Compass Travel business rules is to develop an action page to validate the data collected on the data entry form. The action page receives a form variable for every field on the form that contains a value. You use the cfif
tag to test the values of these fields to ensure that they adhere to Compass Travel business policy.
The cfif
tag lets you create conditions that evaluate to either True or False. Therefore, to use the cfif
tag to test whether a trip name was entered (business rule 1)on the Trip Edit form, you code the following cfif
statement:
<cfif Form.tripName EQ "">
<cfoutput> Trip Name cannot be blank. </cfoutput>
</cfif>
In the previous example, the cfif
statement tests to see if the value of the form variable tripName
is blank. If the trip name condition evaluates to True, ColdFusion sends "Trip name cannot be blank" to the browser.
Note: The keyword EQ
is an operator used to test for equality. For more information about the cfif
tag and its operators, see Developing ColdFusion MX Applications with CFML.
Business rule 8 in the Compass Travel new trip policy requires you to test the value of the depositRequired
check box form variable. Check box and radio button variables are only passed to the action page when the user selects these options on the form. Therefore, an error occurs if the action page tries to use a variable that was not been passed.
To insure an error does not occur, you will use the IsDefined
function in a cfif
statement to determine whether the user selected the Deposit Required check box option on the form:
<cfif not IsDefined("Form.depositRequired")>
<cfset form.depositRequired = "No">
</cfif>
The cfif
statement and the IsDefined
function evaluate the value of the form variable depositRequired
to determine if a value exists. The statement not
IsDefined
returns True if the specified variable is not found and the cfset
statement sets the form variable to No. No indicates a deposit is not required; Yes indicates a deposit is required.
The purpose of the tripeditaction.cfm action page is to update the Compass Travel database, so it is important to make certain that all the business rules are passed successfully before the database insert is executed. Failure of any one of the rules negates the insert.
One approach to ensuring that the action page considers each business rule is to create a local variable with a cfset
tag within the action page that tests to see if any of the business rules failed.
The cfset
tag lets you manipulate the value of a variable. For example, the following pseudocode initializes a variable to a specific value and checks the value using the cfif
statement:
<cfset isOk = "Yes">
if rule 1 fails then <cfset isOK = "No" ... if Rule n fails then <cfset isOk = "No"> ... <cfif isOk = "Yes"> update the database
</cfif>
In the previous example, cfset
initializes the local variable isOk
to Yes.
If any rule fails, the variable isOK
is set to No. The code then tests if isOk
equals Yes, before executing the SQL insert logic.
For more information about using the cfset
and cfif
tags and the IsDefined
function, see Developing ColdFusion MX Applications with CFML or the CFML Reference.
In this exercise you build an action page (tripeditaction.cfm)to validate the data passed to the ColdFusion Server from the tripedit.cfm data entry page. You use the cfif
and cfset
tags to build edits that ensure the data passed is valid per the Compass Travel business rules. Additionally, you will use the ColdFusion IsDefined
function to check to see if data was entered in the data entry form (tripedit.cfm).
<html> <head> <title>Untitled</title> </head> <body> </body> </html>
<html>
tag on the tripeditaction.cfm page. For your convenience, business rule 7 is repeated.Business rule 7: The trip's price and base cost are required. Both values are positive numeric values. The trip price must have at least a 20% markup over base cost.
<!--- Base Cost is Required and must be Numeric ---> <cfif Form.baseCost EQ "" or IsNumeric(Form.baseCost) EQ False> <cfset IsOk = "No"> <cfoutput>Base cost must be a number and cannot be blank.</cfoutput> <cfelse> <!--- Price must be 20% greater than Base Cost ---> <cfif Form.baseCost * 1.2 GT #Form.price#> <cfset IsOk = "No"> <cfoutput>Price must be marked up at least 20% above cost.</cfoutput><br> </cfif> </cfif>
Note: The code for business rule 7 uses ColdFusion cfif
and cfelse
conditional processing tags. The code inside the cfif
tags only executes when the condition evaluates to True. To perform other actions when the condition evaluates to False, the cfelse
tag is used. For more information about using conditional processing tags, see Developing ColdFusion MX Applications with CFML.
The trip price error message displays: "Price must be marked up at least 20% above cost."
<html>
tag. As an example, see the tripeditaction1.cfm page in the solutions directory.Tip: You can either modify your new tripeditaction.cfm page to include the code necessary to meet all 10 business rules or you can copy the tripeditaction1.cfm page from the solutions directory to tripeditaction.cfm in the my_app directory.
Testing recommendations:
Trip name cannot be blank. A trip leader must be specified. Photo file name must be specified. The number of people must be a number and cannot be blank. Trip location cannot be blank. Base cost must be a number and cannot be blank. Price must be a number and cannot be blank.
Validating data on the server-side has two drawbacks. First, since the action page is used for validation, the form page is not in the browser context when the error is trapped. The user will, therefore, not get immediate feedback from the page where the data was entered. Second, because data capture occurs on the client and validation on the server, the number of round-trips to the server is increased. This can cause increased traffic on the network and the server. If the data is validated on the client, then only valid data is posted to the server and traffic is reduced.
An alternative approach to server-side editing is to use client-side scripting. Client-side scripting lets you validate the form data entered on the client prior to posting it to the server. CFML provides alternative versions of standard HTML form tags that provide advantages of client-side data validation.These data input tags include cfinput text
, cfinput radio
, cfinput checkbox
, cfselect
, and others.
Among the improvements over standard HTML tags, ColdFusion form tags offer the following attributes:
To use the improved form tags, you must replace the HTML form tag with the cfform
tag. The following code snippets show the use of the improved ColdFusion form tags. The first code snippet shows how the duration field is validated on the server. The second code snippet shows how ColdFusion form tags simplify field validation on the client.
The following code is on the client (tripedit.cfm page):
<input size=3 name=duration>
Code on the server (tripeditaction.cfm page): <!--- Duration is Required and must be Numeric ---> <cfif Form.numberPeople EQ "" or IsNumeric(Form.numberPeople) EQ False> <cfset IsOk = "No"> <cfoutput>The number of people must be a number and cannot be blank. </cfoutput>
</cfif>
The following code is on the client:
<cfinput name="duration" message="Duration must be a number and cannot be blank." validate="integer" required="Yes" size="3" maxlength="3">
In this exercise, you will use the ColdFusion form tags to move the validation of many business rules from the server to the client. To do this, you will change the HTML form tags in the tripedit.cfm page to ColdFusion form tags that validate these fields on the client side. Next, you will remove the unneeded server-side single-field validation code from tripeditaction.cfm page. Finally, you will test the form to ensure the client side validation is working correctly.
<form>
and </form>
tags to <cfform>
and </cfform>,
respectively.
<cfinput>
tags and <select>
tags to <cfselect>
tags. Note that the input type for the Submit button must remain a standard input rather than cfinput
.
cfinput
, and cfselect
), assign the appropriate values:
For example, the Trip Name field requires the following code:
<cfinput maxlength = "50" size = "50" required = "Yes" name= "tripName" message = "Trip name must not be blank">
Tip: For additional help, review the completed code in the tripedit2.cfm within the solutions directory. For more details about using ColdFusion form tags and their attributes, see Developing ColdFusion MX Applications with CFML.
Tip: You can either remove the single-field validations yourself or use tripeditaction2.cfm file in the solutions directory. The file tripeditaction2.cfm in the solutions directory is a copy of tripeditaction with the single-field edits deleted. Copy tripeditaction2.cfm in the solutions directory to tripeditaction.cfm in the my_app directory.
The modified tripeditaction.cfm page appears as follows:
<!--- Action Page to edit and save Trip information for Compass Travel. ---> <!--- Single field edits have been removed in favor of client-side edits. ---> <!--- Make the passportRequired variable be No if it is not set (check box is empty) ---> <cfset isOk = "Yes"> <cfif not isdefined("Form.depositRequired")> <cfset form.depositRequired = "No"> </cfif> <cfif Form.price GT 750 AND Form.depositRequired EQ "No"> <cfset IsOk = "No"> <cfoutput>Deposit is required for trips priced over $750.</cfoutput> </cfif> <cfif Form.basecost * 1.2 GT #Form.price#> <cfset IsOk = "No"> <cfoutput>Price must be marked up at least 20% above cost.</cfoutput> </cfif> <cfif form.departureDate GT form.returnDate> <cfset isOk = "No"> <cfoutput>Return date cannot precede departure date. Please re-enter.</cfoutput> </cfif><html> <head> <title>Trip Maintenance Confirmation</title> </head> <body> <cfif isOk EQ "Yes"> <h1>Trip Added</h1> <cfoutput>You have added #Form.TripName# to the trips database. </cfoutput> </cfif> </body> </html>
Testing recommendations:
Currently the event types in tripedit.cfm are hard coded:
<!--- Field: eventType --->
<tr> <td valign="top"> Type of Event </td> <td> <cfselect size="1" name="eventType" required="Yes" message="Type of event must be selected."> <option value="1" selected>Surfing</option> <option value="2">Mountain Climbing</option> <option value="3">Mountain Biking</option> </cfselect> </td>
</tr>
As described in Lesson 1, the tutorial application design includes a database table that holds event types. The event type in the Trips table is an identifier used as a foreign key to the eventtypes table (which holds the actual event names). In the previous code, each option
tag contains a value
attribute and option text, such as Surfing
. These values come from the eventtypes table so that they are not hard-coded. The eventtypes table column eventTypeID
is used for the value attribute and the eventType
for the literal value that is displayed in the select box. To retrieve the data from this table, you must include the following cfquery
:
<cfquery name="GetEvents" datasource="CompassTravel">
SELECT eventType, eventTypeID FROM eventtypes
</cfquery>
To exploit the query in the option tags, you can replace the HTML select
tag with cfselect
.
The cfselect
tag is an improved version of the HTML select
tag. Like other ColdFusion form tags, the cfselect
tag provides the required
and message
attributes that validate the data entered. Using the cfselect
tag and the preceding cfquery
, you can implement the eventType
field data entry as follows:
<!--- Field: eventType --->
<tr> <td valign="top"> Type of Event </td> <td> <cfselect size="1" name="eventType" required="Yes" message="Type of event must be selected."> <cfoutput query="GetEvents"> <option value="#GetEvents.eventTypeID#"> #GetEvents.eventType# </option> </cfoutput> /cfselect> </td> </tr>
Do the following steps to modify the Trip Edit page to display a list of event types from the eventtypes table and add validation.
<cfquery name="GetEvents" datasource="CompassTravel"> SELECT eventType, eventTypeID FROM eventtypes </cfquery>
<cfselect size="1" name="eventType" required="Yes" message="Type of event must be selected."> <option value="1" selected>Surfing</option> <option value="2">Mountain Climbing</option> <option value="3">Mountain Biking</option> </cfselect>
with these lines:
<cfselect size="1" name="eventType" required="Yes" message="Type of event must be selected."> <cfoutput query="GetEvents"> <option value="#GetEvents.eventTypeID#"> #GetEvents.eventType# </option> </cfoutput> </cfselect>
If you were interested in moving as much of the business rule logic to the client as possible, you might use other client-side scripting languages, such as JavaScript. By exploiting ColdFusion form tags, you moved most of the responsibility for the business rule checking from the server to the client. This section explains how to migrate cross-field business rules to the client using JavaScript.
Web browsers can execute scripts based on events triggered on the current page. One of the most popular scripting languages is JavaScript. ColdFusion Form tags include an onValidate
attribute that lets you specify your own JavaScript function for custom validation.
The JavaScript form object, input object, and input object value are passed to the specified JavaScript function. The function returns True if validation succeeds and False otherwise. The onValidate
and validate
attributes are mutually exclusive.
Recall the Compass Travel New Trip business rule 6:
The trip departure and return dates must be specified for each trip. All trip dates must be valid future dates. Departure date must precede return date. |
The trip departure and return dates must be specified for each trip.
All trip dates must be valid future dates. Departure date must precede return date.
One reason this rule is a good candidate for a JavaScript function is that the test for a future date cannot be done using the ColdFusion form tags attributes such as validate
and range
. The following JavaScript function (isitFutureDate
) tests whether a date is a valid future date.
function isitFutureDate(oForm, oTag, dateString) {
/* function isitFutureDate parameters: oForm, oTag, dateString returns: boolean oForm is the CFForm object. All onvalidate calls pass this argument. This function ignores it. oTag is the CFForm current tag object. All onvalidate calls pass this argument. This function ignores it. dateString is the value of the current tag object. It should be a date passed as a string in the following format: MM/DD/YYYY. This means that months and days require leading zeros!! Returns true if the date passed is greater than today's date Returns false if the date passed is NOT greater than todays date. */ // Check to make sure the date is zero filled with 4 digit year and //therefore 10 characters long. if (dateString.length != 10) return false; var now = new Date(); var today = new Date(now.getYear(),now.getMonth(),now.getDate()); var testdate = new Date(dateString.substring(6,10), dateString.substring(0,2)-1, dateString.substring(3,5)); if (testdate > now) return true; else return false;
}
Another reason that rule 6 requires JavaScript scripting is that it tests the values of more than one field in a single edit. You must ensure that the return date field is greater than departure date field. To do this, you add a JavaScript function to validate the trip date range entered, and specify the function on the onValidate
attribute of the returnDate
cfinput
tag.
function validateTripDateRange(oForm, oTag, dateString)
{ /* parameters: oForm, oTag, dateString returns: boolean oForm is the CFForm object. All onvalidate calls pass this argument. This function ignores it. oTag is the CFForm current tag object. All onvalidate calls pass this argument. This function ignores it. dateString is the value of the current tag object. It should be a date passed as a string in the following format: MM/DD/YYYY. This means that months and days require leading zeros!! Returns true if the date passed is a future date greater than the departure date Returns false if the date passed is NOT a future date greater than departure date. */ //Edit to make sure that Return date is Later than departure date. var returnDateString; //First check to see if the departure Date is a valid future date if (isitFutureDate(oForm, oTag, dateString) == false) return false; var departureDate = new Date(dateString.substring(6,10), dateString.substring(0,2)-1, dateString.substring(3,5)); returnDateString = document.forms(0).item("returnDate").value; var returnDate = new Date(returnDateString.substring(6,10), returnDateString.substring(0,2)-1, returnDateString.substring(3,5)); if (departureDate < returnDate) return true; else return false;
}
The important point about the preceding JavaScript is that you can use two functions, isitFutureDate
and validateTripDateRange
, to verify whether a date is in the future and the trip date range is valid, respectively.
In this exercise you will modify the Trip Insert page to validate the departure and return dates using the JavaScript functions provided.
Copy example code provided Copy the tripsedit3.cfm file from the solutions directory and rename it to tripedit.cfm in the my_app subdirectory
or
Add JavaScript-based validation code to tripedit.cfm Follow these steps to modify the tripedit.cfm page:
body
tag in tripedit.cfm.
departureDate
and returnDate
input tags to include the onValidate
attributes as follows:<cfinput name="departureDate" size="10" validate="date" onvalidate="isitFutureDate" message="Departure date must be a valid future date (mm/dd/yyyy)."> <cfinput size="10" name="returnDate" validate="date" onvalidate="validateTripDateRange" message="Return date must be a valid date greater than departure date (mm/dd/yyyy).">
You would expect the application to reject this date. It does not. This is because the validate
attribute of a cfinput
tag (returnDate in this example) is ignored when there is a JavaScript routine specified in the onValidate
attribute. To correct this, you must write a test to validate the date using JavaScript (not addressed in this tutorial).
At this point, you have a more efficient application. The client is handling much of the validation of the Compass Travel new trip business rules. Except for the trip photo file, the server receives only valid data.
The trip photo file business rule does not fit nicely into this design, however. The last trip photo business rule has two parts:
You used the required
attribute for the photo cfinput
tag to ensure that a file name is entered. Now you must make sure that the file exists in the right directory so the application can display it to customers.
Since browser clients are prohibited from doing standard file I/O (input/output) on the web server, the Trips Maintenance application will use server-side validation to ensure the existence of the photo file. You will add the business rule for the photo file to the tripeditaction.cfm page.
To check to see if a file exists, ColdFusion provides a FileExists
function. The syntax of this function is:
FileExists(absolute_path)
This function returns True
if the file specified in the argument does exist; otherwise, it returns False
. Assume that a data entry page has an input tag named "testFileName". If a user types in a valid file name, the action page snippet from the following example would display the message "The test file exists":
<cfset fileLocation = "c:\inetpub\wwwroot\images\">
<cfif IsDefined("form.testFileName")> <cfif form.testFileName is not ""> <!---Concatenate the File Location with the FileName passed in---> <cfset fileLocation = fileLocation & form.testFileName> <cfif FileExists(fileLocation)> <cfoutput> The test file exists. </cfoutput> </cfif> </cfif>
</cfif>
Note: The trip photo images are stored in the following path relative to your web root directory: \cfdocs\getting_ started\photos. Therefore, if your web root is C:\inetpub\wwwroot, then the photos are stored in the C:\inetpub\wwwroot\cfdocs\getting_ started\photos directory.
For more information about the FileExists
function, see CFML Reference.
The following table describes the code used to verify whether a file exists:
In this exercise, you will use the Cold Fusion FileExists
function to ensure that the photo file name entered in the Trip Edit page exists in the location specified.
For example, depending on your web server configuration, the photolocation path might be:
<cfset PhotoLocation
"C:\cfusionmx\wwwroot\CFDOCS\getting_started\Photos\">
or
<cfset PhotoLocation =
"C:\Inetpub\wwwroot\CFDOCS\getting_started\Photos\">
<cfset PhotoLocation =
"/opt/coldfusionmx/wwwroot/cfdocs/getting_started/photos/">
or
<cfset PhotoLocation =
"/<webserverdocroot>/cfdocs/getting_started/photos/">
Testing recommendations:
The following error message appears: Trip photo does not exist.
The following message appears: Trip added.