Macromedia Coldfusion MX 7 Web Application Construction Kit

So far in this chapter, you have seen how ColdFusion's Web application framework features help you maintain a consistent look and feel throughout your application. You've also seen how easy it is to set up "before and after" processing with the special Application.cfc component and the onRequestStart and onRequestEnd methods. In other words, your pages are starting to look and behave cohesively.

Next, you will learn how your application's templates can start sharing variables between page requests. Basically, this is the part where your application gets a piece of the server's memory in which to store values. This is where it gets a brain.

What Are Application Variables?

Pretend it's Oscar season. Orange Whip Studios feels that all of its films are contenders. Tensions are high, and the president wants a new "Featured Movie" box on the studio's home page to help create more "buzz" than its bitter rival, Miramax. The featured movie should be different each time the home page is viewed, shamelessly rotating through all of the studio's movies. It's your job to get this project done, pronto.

Hmmm. You could retrieve all the movies from the database for each page request and somehow pick one at random, but that wouldn't guarantee that the same movie wouldn't get picked three or four times in a row. What you want is some way to remember your current spot in the list of movies, so they all get shown evenly, in order. You consider making a table to remember which movies have been shown and then deleting all rows from the table when it's time to rotate through them again, but that seems like overkill. You wish there was some kind of variable that would persist between page requests, instead of dying at the bottom of each page like the ColdFusion variables you're used to.

Well, that's exactly what application variables are for. Instead of setting a variable called lastMovieID, you could call it APPLICATION.lastMovieID. After you set this variable value to 5, say, it remains set at 5 until you change it again (or until the server is restarted). In essence, application variables let you set aside a little piece of ColdFusion's memory that your application can use for its own purposes.

When to Use Application Variables

Generally, you can use application variables whenever you need a variable to be shared among all pages and all visitors to your application. The variable is kept in ColdFusion's memory, and any page in your application can access or change its value. If some code on one of your pages changes the value of an application variable, the next hit to any of your application's pages will reflect the new value.

NOTE

This means you should not use application variables if you want a separate copy of the variable to exist for each visitor to your site. In other words, application variables shouldn't be used for anything personalized, because they don't distinguish between your site's visitors.

Chapter 20, "Working with Sessions," explains how to create variables that are maintained separately for each visitor.

Consider application variables for:

  • Rotating banner ads evenly, so that all ads get shown the same number of times

  • Rotating other types of content, such as the featured movie problem mentioned previously, or products that might be on sale

  • Keeping counters of various types of events, such as the number of people currently online or the number of hits since the server was started

  • Maintaining some type of information that changes only occasionally or perhaps doesn't change at all, but can take time to compute or retrieve

Do not use application variables for per-user tasks, such as these:

  • Maintaining a shopping cart

  • Remembering a user's email address or username from visit to visit

  • Keeping a history of the pages a user has visited while he has been on your site

Using the Application.cfc Component

We've already discussed the special purpose of the Application.cfc file. What we didn't mention was that by including an Application.cfc file, you automatically enable the use of Application variables. Application variables are one type of persistent variable; you will learn about two other typesclient and session variablesin Chapter 20.

So far, the example Application.cfc files we have shown have only demonstrated methods. Components can also contain data. There are two main scopes in a component used to store data: VARIABLES and THIS. Earlier we mentioned that components can use any method names you want. But in the Application.cfc component, some method names were special. The same applies to the THIS cope. By setting particular values in the THIS scope you can control how the application behaves. For now we will focus on just two of those values, demonstrated in table 19.2.

NOTE

ColdFusion maintains your application variables based on the THIS scopes NAME value. Therefore, it's important that no other applications on the same ColdFusion server use the same NAME. If they do, ColdFusion will consider them to be the same application and will share the variables among all the combined pages. Changing a variable in one also changes it in the other, and so on. It's conceivable to find yourself in a situation where this is actually what you want (if for some reason all the pages in your application simply can't be nested within a single folder); otherwise, make sure that each Application.cfc's THIS scope gets its own NAME.

Now that application variables have been enabled, using them is quite simple. Basically, you create or set an application variable the same way you would set a normal variable, generally using the <cfset> tag. The only difference is the presence of the word APPLICATION, followed by a dot. For instance, the following line would set the APPLICATION.ourHitCount variable to 0. The variable would then be available to all pages in the application and would hold the value of 0 until it was changed:

<cfset APPLICATION.ourHitCount = 0>

You can use application variables in any of the same places you would use ordinary ones. For instance, the following code adds one to an application variable and then outputs the new value, rounded to the nearest thousandth:

<cfset APPLICATION.ourHitCount = APPLICATION.ourHitCount + 1> <cfoutput>#round(APPLICATION.ourHitCount / 1000)># thousand</cfoutput>

You also can use application variables with ColdFusion tags, such as <cfif>, <cfparam>, and <cfoutput>. See Chapter 8, "Using ColdFusion," and Chapter 9, "CFML Basics," if you want to review the use of variables in general.

Initializing Application Variables

Application variables are persistent. That simply means that once you create them, they stick around. Because of this, there is no reason to set them on every request. Once you create an Application variable, you don't need to create it. Once simple way to handle that would be with the isDefined() function.

<cfif not isDefined("APPLICATION.dsn")> <cfset APPLICATION.dsn = "ows"> </cfif>

This code will check to see if the variable, APPLICATION.dsn exists. If it doesn't, it will create it. However, the Application.cfc component provides an even easier way to do this. One of the special methods mention in table 19.1 is the onApplicationStart() method. This method will execute only when the application starts. Conversely, there is also an onApplicationEnd() method. This could be used to do a variety of things. Listing 19.6 shows a newer version of the Application.cfc worked on earlier.

If you save this file, be sure to save it as Application.cfc, not Application3.cfc.

Listing 19.6. Application3.cfcUsing onApplicationStart and onApplicationEnd

<!--- Filename: Application.cfc (The "Application Component") Created by: Raymond Camden (ray@camdenfamilyc.om) Purpose: Sets "constant" variables and includes consistent header ---> <cfcomponent output="false"> <cfset THIS.name = "ows23"> <cffunction name="onApplicationStart" returnType="boolean" output="false"> <!--- When did the application start? ---> <cfset APPLICATION.appStarted = now()> <cfreturn true> </cffunction> <cffunction name="onApplicationEnd" returnType="void" output="false"> <cfargument name="appScope" required="true"> <!--- Log how many minutes the application stayed alive ---> <cflog file="#THIS.name#" text= "App ended after #dateDiff('n',ARGUMENTS.appScope.appStarted,now())# minutes."> </cffunction> <cffunction name="onRequestStart" returnType="boolean" output="true"> <!--- Any variables set here can be used by all our pages ---> <cfset request.dataSource = "ows"> <cfset request.companyName = "Orange Whip Studios"> <!--- Display our Site Header at top of every page ---> <cfinclude template="SiteHeader.cfm"> <cfreturn true> </cffunction> <cffunction name="onRequestEnd" returnType="void" output="true"> <!--- Display our Site Footer at bottom of every page ---> <cfinclude template="SiteFooter.cfm"> </cffunction> </cfcomponent>

There's a lot of new code here, so let's tackle it bit by bit. The first new line is:

<cfset THIS.name = "ows23">

This line uses the THIS scope to name the application. Remember, every name for your application should be unique. If you use the same name for multiple Application.cfc files, they will essentially act as the same application. Notice that this line of code is outside any method. This line will be run when the Application.cfc file is loaded by ColdFusion.

The next set of new code is the onApplicationStart method. This method really only does one thingit creates a variable called APPLICATION.appStarted initialized with the current time. The idea is to simply store the time the application started.

Next we have the onApplicationEnd method. This method will fire when the application ends. This is something brand new in ColdFusion. Normally the only way to execute ColdFusion code automatically is with the ColdFusion Scheduler. Outside of that, ColdFusion code only executes when someone requests a file. The onApplicationEnd method (as well as the onSessionEnd method) run without anyone actually requesting a ColdFusion document.

That said, you can't output anything from this method. Even if you did, no one could see it! What you can do is clean up the application. This can include logging information to a database or file, firing off an email, or doing any number of things that would make sense when an application ends. Let's examine the method line by line. The first line is:

<cfargument name="appScope" required="true">

This simply defines an argument sent to the method. In our case, the ColdFusion server automatically sends a copy of the Application scope (all the data you stored in it) to the onApplicationEnd method. This is important. You can't access the APPLICATION scope they way you can normally. Instead, you have to use the copy passed in the method. The next line will show an example of this:

<cflog file="#THIS.name#" text= "App ended after #dateDiff('n',ARGUMENTS.appScope.appStarted,now())# minutes.">

The <cflog> tag simply logs information to a file. We are only using two of the attributes in this line. The file attribute simply tells <cflog> what name to use for the file. When providing a file name, you don't add the ".log" to the name; <cflog> will do that for you. In our code, we use the value of the Application's name. Recall that we set the name using the THIS scope earlier in the component. The text attribute defines what is sent to the file. If you remember, we stored the time the application loaded in a variable called APPLICATION.appStarted. As we said above, we can't access the Application scope in the onApplicationEnd method. Instead, we have to use the copy passed in. We called this argument appScope, so we can access our original value as ARGUMENTS.appScope.appStarted. We use the dateDiff function to return the number of minutes between when the application started and the current time. This lets us log the total time the application was running before it timed out.

The rest of the file simply duplicates the onRequestStart and onRequestEnd method's we described earlier.

Putting Application Variables to Work

Application variables can make it relatively easy to get the little featured movie widget up and running. Again, the idea is for a callout-style box, which cycles through each of Orange Whip Studio's films, to display on the site's home page. The box should change each time the page is accessed, rotating evenly through all the movies.

Listing 19.7 shows one simple way to get this done, using application variables. Note that the template is broken into two separate parts. The first half is the interesting part, in which an application variable called MovieList is used to rotate the featured movie correctly. The second half simply outputs the name and description to the page, as shown in Figure 19.3.

Listing 19.7. FeaturedMovie.cfmUsing Application Variables to Track Content Rotation

<!--- Filename: FeaturedMovie.cfm Created by: Nate Weiss (NMW) Purpose: Displays a single movie on the page, on a rotating basis Please Note Application variables must be enabled ---> <!--- List of movies to show (list starts out empty) ---> <cfparam name="APPLICATION.movieList" type="string" default=""> <!--- If this is the first time we're running this, ---> <!--- Or we have run out of movies to rotate through ---> <cfif listLen(APPLICATION.movieList) eq 0> <!--- Get all current FilmIDs from the database ---> <cfquery name="getFilmIDs" datasource="#REQUEST.dataSource#"> SELECT FilmID FROM Films ORDER BY MovieTitle </cfquery> <!--- Turn FilmIDs into a simple comma-separated list ---> <cfset APPLICATION.movieList = valueList(getFilmIDs.FilmID)> </cfif> <!--- Pick the first movie in the list to show right now ---> <cfset thisMovieID = listGetAt(APPLICATION.MovieList, 1)> <!--- Re-save the list, as all movies *except* the first ---> <cfset APPLICATION.movieList = listDeleteAt(APPLICATION.movieList, 1)> <!--- Now that we have chosen the film to "Feature", ---> <!--- Get all important info about it from database. ---> <cfquery name="GetFilm" datasource="#REQUEST.dataSource#"> SELECT MovieTitle, Summary, Rating, AmountBudgeted, DateInTheaters FROM Films f, FilmsRatings r WHERE FilmID = #thisMovieID# AND f.RatingID = r.RatingID </cfquery> <!--- Now Display Our Featured Movie ---> <cfoutput> <!--- Define formatting for our "feature" display ---> <style type="text/css"> TH.fm {background:RoyalBlue;color:white;text-align:left; font-family:sans-serif;font-size:10px} TD.fm {background:LightSteelBlue; font-family:sans-serif;font-size:12px} </style> <!--- Show info about featured movie in HTML Table ---> <table width="150" align="right" border="0" cellspacing="0"> <tr><th > Featured Film </th></tr> <!--- Movie Title, Summary, Rating ---> <tr><td > <b>#getFilm.MovieTitle#</b><br> #getFilm.Summary#<br> <p align="right">Rated: #getFilm.Rating#</p> </td></tr> <!--- Cost (rounded to millions), release date ---> <tr><th > Production Cost $#round(getFilm.AmountBudgeted / 1000000)# Million<br> In Theaters #dateFormat(getFilm.DateInTheaters, "mmmm d")#<br> </th></tr> </table> <br clear="all"> </cfoutput>

Figure 19.3. Application variables enable the featured movie to be rotated evenly among all page requests.

As you can see, the top half of the template is pretty simple. The idea is to use an application variable called movieList to hold a list of available movies. If 20 movies are in the database, the list holds 20 movie IDs at first. The first time the home page is visited, the first movie is featured and then removed from the list, leaving 19 movies in the list. The next time, the second movie is featured (leaving 18), and so on until all the movies have been featured. Then the process begins again.

Looking at the code line by line, you can see how this actually happens:

The <cfparam> tag is used to set the APPLICATION.movieList variable to an empty string if it doesn't exist already. Because the variable will essentially live forever once set, this line has an effect only the first time this template runs (until the server is restarted).

The <cfif> tag is used to test whether the movieList variable is currently empty. It is empty if this is the first time the template has run or if all the available movies have been featured in rotation already. If the list is empty, it is filled with the list of current movie IDs. Getting the current list is a simple two-step process of querying the database and then using the valueList function to create the list from the query results.

The listGetAt() function is used to get the first movie's ID from the list. The value is placed in the thisMovieID variable. This is the movie to feature on the page.

Finally, the listDeleteAt() function is used to chop off the first movie ID from the APPLICATION.movieList variable. The variable now holds one fewer movie. Eventually, its length will dwindle to zero, in which case the <cfif> tag will again test whether the movieList variable is currently empty, repeating the cycle.

TIP

Because you are interested in the first element in the list, you could use listFirst() in place of the listGetAt() function shown in Listing 16.7, if that reads more clearly for you. You also could use listRest() instead of the listDeleteAt() function.

See Appendix B for details.

Now that the movie to be featured has been picked (it's in the thisMovieID variable), actually displaying the movie's name and other information is straightforward. The <cfquery> in the second half of Listing 19.7 selects the necessary information from the database, and then a simple HTML table is used to display the movie in a nicely formatted box.

At this point, Listing 19.7 can be visited on it's own, but it was really meant to show the featured movie on Orange Whip's home page. Simply include the template using the <cfinclude> tag, as shown in Listing 19.8.

Figure 19.3 shows the results.

Listing 19.8. Index2.cfmIncluding the Featured Movie in the Company's Home Page

<!--- Filename: Index.cfm Created by: Nate Weiss (NMW) Please Note Header and Footer are automatically provided ---> <cfoutput> <p>Hello, and welcome to the home of #REQUEST.companyName# on the web! We certainly hope you enjoy your visit. We take pride in producing movies that are almost as good as the ones they are copied from. We've been doing it for years. On this site, you'll be able to find out about all our classic films from the golden age of Orange Whip Studios, as well as our latest and greatest new releases. Have fun!<br> </cfoutput> <!--- Show a "Featured Movie" ---> <cfinclude template="FeaturedMovie.cfm">

Категории