Macromedia Coldfusion MX 7 Web Application Construction Kit
Client variables are similar to cookies, except that they are stored on the server, rather than on the client (browser) machine. In many situations, you can use the two almost interchangeably. You're already familiar with cookies, so learning how to use client variables will be a snap. Instead of using the COOKIE prefix before a variable name, you simply use the CLIENT prefix. Okay, there's a little bit more to it than that, but not much. NOTE Before you can use the CLIENT prefix, you must enable ColdFusion's Client Management feature. See the section "Enabling Client Variables," later in this chapter.
NOTE It's worth noting that client variables can also be configured so that they are stored on the browser machine, if you take special steps in the ColdFusion Administrator. They then become essentially equivalent to cookies. See the section "Adjusting How Client Variables Are Stored," later in this chapter. How Do Client Variables Work?
Client variables work like this:
For the most part, this process is hidden to you as a developer. You simply use the CLIENT scope prefix in your code; ColdFusion takes care of the rest. Enabling Client Variables
Before you can use client variables in your code, you must enable them using an Application.cfc file. In the last chapter, you learned how to use this file to enable application variables. You can modify the behavior of the application using This-scope variables. Table 20.3 lists values relevant to client variables.
For now, just concentrate on the clientManagement attribute (the others are discussed later). Listing 20.5 shows how easy it is to enable client variables for your application. After you save this code in the Application.cfc file for your application, you can start using client variables. (Be sure to save Listing 20.5 as Application.cfc, not Application2.cfc.) NOTE If you attempt to use client variables without enabling them first, an error message will be displayed. Listing 20.5. Application2.cfcEnabling Client Variables in Application.cfc
<!--- Filename: Application.cfc Created by: Raymond Camden (ray@camdenfamily.com) Handles application events. ---> <cfcomponent output="false"> <cfset this.name="OrangeWhipSite"> <cfset this.clientManagement=true> </cfcomponent>
Client variables are ideal for storing things like user preferences, recent form entries, and other types of values that you don't want to force your users to provide over and over again. Remembering Values for Next Time
Consider a typical search form, in which the user types what they are looking for and then submits the form to see the search results. It might be nice if the form could remember what the user's last search was. The code in Listing 20.6 lets it do just that. The basic idea is that the form's search criteria field will already be filled in, using the value of a variable called SearchPreFill. The value of this variable is set at the top of the page and will be set to the last search the user ran, if available. If no last search information exists (if this is the first time the user has used this page), it will be blank. Listing 20.6. SearchForm1.cfmUsing Client Variables to Remember the User's Last Search
<!--- Filename: SearchForm1.cfm Created by: Nate Weiss (NMW) Please Note Maintains "last" search via Client variables ---> <!--- Determine value for "Search Prefill" feature ---> <!--- When user submits form, save search criteria in client variable ---> <cfif isDefined("FORM.searchCriteria")> <cfset CLIENT.lastSearch = FORM.searchCriteria> <cfset searchPreFill = FORM.searchCriteria> <!--- If not submitting yet, get prior search word (if possible) ---> <cfelseif isDefined("CLIENT.lastSearch")> <CFSET searchPreFill = CLIENT.lastSearch> <!--- If no prior search criteria exists, just show empty string ---> <cfelse> <cfset searchPreFill = ""> </cfif> <html> <head><title>Search Orange Whip</title></head> <body> <h2>Search Orange Whip</h2> <!--- Simple search form, which submits back to this page ---> <cfform action="#cgi.script_name#" method="post"> <!--- "Search Criteria" field ---> Search For: <cfinput name="SearchCriteria" value="#searchPreFill#" required="Yes" message="You must type something to search for!"> <!--- Submit button ---> <input type="submit" value="Search"><br> </cfform> </body> </html> The first part of this template (the <cfif> part) does most of the work because it's in charge of setting the searchPreFill variable that provides the "last search" memory for the user. There are three different conditions to deal with. If the user currently is submitting the form to run the search, their search criteria should be saved in a client variable called CLIENT.lastSearch. If the user isn't currently submitting the form but has run a search in the past, their last search criteria should be retrieved from the lastSearch client variable. If no last search is available, the isDefined("CLIENT.lastSearch") test will fail, and searchPreFill should just be set to an empty string. The rest of the code is an ordinary form. Note, though, that the value of the searchPreFill variable is passed to the <cfinput> tag, which presents the user with the search field. If you visit this page in your browser for the first time, the search field will be blank. To test the use of client variables, type a word or two to search for and submit the form. Of course, no actual search takes place because no database code yet exists in the example, but the form should correctly remember the search criteria you typed. You can close the browser and reopen it, and the value should still be there. NOTE Assuming that you haven't changed anything in the ColdFusion Administrator to the contrary, the value of CLIENT. LastSearch will continue to be remembered until the user is away from the site for 90 days.
Using Several Client Variables Together
No limit is set on the number of client variables you can use. Listing 20.7 builds on the search form from Listing 20.6, this time allowing the user to specify the number of records the search should return. A second client variable, called lastMaxRows, remembers the value, using the same simple <cfif> logic shown in the previous listing. Listing 20.7. SearchForm2.cfmUsing Client Variables to Remember Search Preferences
<!--- Filename: SearchForm2.cfm Created by: Nate Weiss (NMW) Please Note Maintains "last" search via Client variables ---> <!--- When user submits form, save search criteria in Client variable ---> <cfif isDefined("FORM.searchCriteria")> <cfset CLIENT.lastSearch = FORM.searchCriteria> <cfset CLIENT.lastMaxRows = FORM.searchMaxRows> <!--- if not submitting yet, get prior search word (if possible) ---> <cfelseif isDefined("CLIENT.lastSearch") and isDefined("CLIENT.lastMaxRows")> <cfset searchCriteria = CLIENT.lastSearch> <cfset searchMaxRows = CLIENT.lastMaxRows> <!--- if no prior search criteria exists, just show empty string ---> <cfelse> <cfset searchCriteria = ""> <cfset searchMaxRows = 10> </cfif> <html> <head><title>Search Orange Whip</title></head> <body> <h2>Search Orange Whip</h2> <!--- Simple search form, which submits back to this page ---> <cfform action="#cgi.script_name#" method="post"> <!--- "Search Criteria" field ---> Search For: <cfinput name="SearchCriteria" value="#searchCriteria#" required="Yes" message="You must type something to search for!"> <!--- Submit button ---> <input type="Submit" value="Search"><br> <!--- "Max Matches" field ---> <i>show up to <cfinput name="SearchMaxRows" value="#searchMaxRows#" size="2" required="Yes" validate="integer" range="1,500" message="Provide a number from 1-500 for search maximum."> matches</i><br> </cfform> <!--- If we have something to search for, do it now ---> <cfif searchCriteria neq ""> <!--- Get matching film entries from database ---> <cfquery name="getMatches" datasource="ows"> SELECT FilmID, MovieTitle, Summary FROM Films WHERE MovieTitle LIKE '%#SearchCriteria#%' OR Summary LIKE '%#SearchCriteria#%' ORDER BY MovieTitle </cfquery> <!--- Show number of matches ---> <cfoutput> <hr><i>#getMatches.recordCount# records found for "#searchCriteria#"</i><br> </cfoutput> <!--- Show matches, up to maximum number of rows ---> <cfoutput query="getMatches" maxrows="#searchMaxRows#"> <p><b>#MovieTitle#</b><br> #Summary#<br> </cfoutput> </cfif> </body> </html>
Next, the actual search is performed, using simple LIKE code in a <cfquery> tag. When the results are output, the user's maximum records preference is provided to the <cfoutput> tag's maxrows attribute. Any rows beyond the preferred maximum aren't shown. (If you want to brush up on the <cfquery> and <cfoutput> code used here, see Chapter 10, "Creating Data-Driven Pages.") Not only does this version of the template remember the user's last search criteria, but it also actually reruns the user's last query before they even submit the form. This means the user's last search results will be redisplayed each time they visit the page, making the search results appear to be persistent. The results are shown in Figure 20.3. Figure 20.3. Client variables make maintaining the state of a user's recent activity easy.
You easily could change this behavior by changing the second <cfif> test to isDefined("FORM. SearchCriteria"). The last search would still appear prefilled in the search form, but the search itself wouldn't be rerun until the user clicked the Search button. Use client variables in whatever way makes sense for your application. TIP To improve performance, you could add a cachedwithin or cachedafter attribute to the <cfquery> tag, which enables ColdFusion to deliver any repeat searches directly from the server's RAM memory.
Deleting Client Variables
Once set, client variables are stored semi-permanently: they're deleted only if a user's browser doesn't return to your site for 90 days. In the next section, you learn how to adjust the number of days that the variables are kept, but sometimes you will need to delete a client variable programmatically. NOTE It's important to understand that a client doesn't have its own expiration date. Client variables don't expire individually; the whole client record is what expires. So, it's not that a client variable is deleted 90 days after it is set. Rather, the client variable (and all other client variables assigned to the user's machine) is deleted after the user lets 90 days pass before revisiting any pages in the application. For more information about tweaking the expiration system, see "Adjusting How Long Client Variables Are Kept," in the next section.
ColdFusion provides a deleteClientVariable() function, which enables you to delete individual client variables by name. The function takes one argument: the name of the client variable you want to delete (the name isn't case sensitive). Another handy housekeeping feature is the getClientVariablesList() function, which returns a comma-separated list of the client-variable names that have been set for the current browser. Listing 20.8 shows how these two functions can be used together to delete all client variables that have been set for a user's browser. You could use code such as this on a start-over type of page, or if the user has chosen to log out of a special area. Listing 20.8. DeleteClientVars.cfm Deleting Client Variables Set for the Current Browser
<!--- Filename: DeleteClientVars.cfm Created by: Nate Weiss (NMW) Purpose: Deletes all client variables associated with browser ---> <html> <head><title>Clearing Your Preferences</title></head> <body> <h2>Clearing Your Preferences</h2> <!--- For each client-variable set for this browser... ---> <cfloop list="#getClientVariablesList()#" index="thisVarName"> <!--- Go ahead and delete the client variable! ---> <cfset deleteClientVariable(thisVarName)> <cfoutput>#thisVarName# deleted.<br></cfoutput> </cfloop> <p>Your preferences have been cleared.</p> </body> </html>
Along with deleteClientVariable(), you can also treat the CLIENT scope like a structure. So for example, you can remove the client variable name using structDelete(CLIENT,"name"). Adjusting How Client Variables Are Stored
Out of the box, ColdFusion stores client variables in the server's Registry and will delete all client variables for any visitors who don't return to your site for 90 or more days. You can, of course, tweak these behaviors to suit your needs. This section discusses the client-variable storage options available. Adjusting How Long Client Variables Are Kept
Normally, client variables are maintained on what amounts to a permanent basis for users who visit your site at least once every 90 days. If a user actually lets 90 days pass without visiting your site (for shame!), all of their client variables are purged by ColdFusion. This helps keep the client-variable store from becoming ridiculously large. To adjust this value from the default of 90 days, do the following:
NOTE Remember, there isn't a separate time-out for each client variable. The only time client variables are automatically purged is if the client browser hasn't visited the server at all for 90 days (or whatever the purge-data setting has been set to).
Storing Client Variables in a Database
ColdFusion can store your client variables in a database instead of in the Registry. This will appeal to people who don't like the idea of the Registry being used for storage, or who find that they must make the Registry very large to accommodate the number of client variables they need to maintain. The ability to store client variables in a SQL database is particularly important if you are running several servers in a cluster. You can have all the servers in the cluster keep your application's client variables in the same database, thereby giving you a way to keep variables persistent between pages without worrying about what will happen if the user ends up at a different server in the cluster on their next visit. See the section "Sharing Client Variables Between Servers," later. NOTE When using the term Registry, we are referring to the Windows Registry, assuming that ColdFusion Server is installed on a Windows machine. On other platforms, ColdFusion ships with a simple Registry replacement for storage of client variables. Linux and Unix users can still use the default client storage mechanism of the Registry. However, the Registry replacement isn't a high-performance beast, and isn't recommended for applications that get a lot of traffic.
To store your client variables in a database, follow these steps:
You now can supply the new data source name to the clientStorage value in the This scope from the Application.cfc file (refer to Table 20.3). All of your application's client variables now will be stored in the database instead of in the Registry. TIP If you go back to the Client Variables page of the ColdFusion Administrator and change the Default Storage Mechanism for Client Sessions value to the data source you just created, it will be used for all applications that don't specify a clientStorage attribute (refer to Table 20.3). Sharing Client Variables Between Servers
As explained at the beginning of this section, ColdFusion tracks each browser by setting its own client-tracking cookie called CFID. Normally, it sets this cookie so that it is sent back only to the server that set it. If you have three ColdFusion servers, each visitor will be given a different CFID number for each server, which in turn means that client variables will be maintained separately for each server. In many situations, especially if you are operating several servers in a cluster, you will want client variables to be shared between the servers, so that a CLIENT.lastSearch variable set by one server will be visible to the others. To share client variables between servers, do the following:
Now you can use client variables in your code as you normally would. No matter which server a user visits, ColdFusion will store all client variables in the common database you set up. NOTE For cookies to be shared between servers, they all must be members of the same top-level Internet domain (for instance, orangewhip.com).
TIP For more information about using client variables in a clustered environment, see the "Managing Session State in Clusters" chapter in the companion volume, Advanced Macromedia ColdFusion MX 7 Application Development (ISBN 0-321-29269-3; Macromedia Press).
Backing Up Your Server's Client Variables
If you are keeping client variables in a database, you can back then all up by simply backing up the database itself. If it's an Access or some other file-based database, that entails making a backup copy of the database (.mdb) file itself. Otherwise, you must use whatever backup facility is provided with your database software. If you are using a Windows server and are keeping client variables in the Registry, you can make a copy of the appropriate portion of the Registry. Just follow these steps:
Storing Client Variables As a Cookie
Somewhat paradoxically, you can tell ColdFusion to store your application's client variables in cookies on the user's machine, rather than on the server side. You do this by setting the clientStorage value in the This scope from the Application.cfc file to Cookie. This basically lets you continue using the CLIENT prefix even if you want the variables to essentially be stored as cookies. This might be useful, for instance, in situations where you are selling your code as a third-party application and want your licensees to have the option of using a server-side or client-side data store. Unfortunately, the size limitations for cookies will apply (see the section "Cookie Limitations," above). This is a somewhat esoteric subject, so it isn't discussed in full here. Please consult the ColdFusion documentation for more information about this feature. NOTE The cookie-storage mechanism for client variables can be useful in a clustered environment or a site that gets an extremely large number of discrete visitors. NOTE For more information about using client variables in a clustered environment, see the "Managing Session State in Clusters" chapter in our companion volume, Advanced Macromedia ColdFusion MX 7 Application Development (ISBN 0-321-29269-3; Macromedia Press).
Using Client Variables Without Requiring Cookies
Above, you learned that ColdFusion maintains the association between a browser and its client variables by storing a CFID cookie on the browser machine. That would seem to imply that client variables won't work if a browser doesn't support cookies or has had them disabled. Don't worry; all isn't completely lost. Actually, ColdFusion normally sets two cookies with which to track client variables: the cfid value already mentioned and a randomly generated cftoken value. Think of cfid and cftoken as being similar to a user name and password, respectively. Only if the cfid and cftoken are both valid will Cold Fusion be capable of successfully looking up the appropriate client variables. If the browser doesn't provide the values, for whatever reason (perhaps because the user has configured the browser not to use cookies or because a firewall between the user and your server is stripping cookies out of each page request), ColdFusion won't be able to look up the browser's client variables. In fact, it will be forced to consider the browser to be a new, first-time visitor, and it will generate a new cfid and cftoken for the browserwhich, of course, means that all client variables that might have been set during previous page visits will be lost. You can still use client variables without requiring cookies, but it takes a bit more work. Basically, you need to make the cfid and cftoken available to ColdFusion yourself, by passing the values manually in the URL to every single page in your application. So, if you want your client variables to work for browsers that don't (or won't) support cookies, you must include the cfid and cftoken as URL parameters. So, a link such as <a href="MyPage.cfm">Click Here</a>
would be changed to the following, which would need to be placed between <cfoutput> tags: <a href="MyPage.cfm?CFID=#CLIENT.cfid#&CFTOKEN=#CLIENT.cftoken#">Click Here</a>
ColdFusion provides a shortcut property you can use to make this task less tedious. Instead of providing the cfid and cftoken in the URL, you can just pass the special CLIENT.urlToken property, which always holds the current cfid and cftoken name/value pairs together in one string, including the & and = signs. This means the previous line of code can be shortened to the following, which would still need to be placed between <cfoutput> tags: <a href="MyPage.cfm?#CLIENT.urlToken#">Click Here</a>
TIP We suggest you always use the urlToken variable as shown here, rather than passing cfid and cftoken separately. This way, you will have fewer changes to make if ColdFusion's method of tracking clients is changed in the future.
You must be sure to pass CLIENT.urlToken in every URL, not just in links. For instance, if you are using a <form> (or <cfform>) tag, you must pass the token value in the form's action, such as this: <form action="MyPage.cfm?#CLIENT.urlToken#" method="Post"> If you are using frames, you must pass the token value in the src attribute, such as this: <frame src="/books/2/448/1/html/2/MyPage.cfm?#CLIENT.urlToken#"> And so on. Basically, you must look through your code and ensure that whenever you see one of your .cfm templates in a URL of any type, you correctly pass the token value. NOTE Remember that the token value must always be placed between <cfoutput> tags, unless the URL is being passed as an attribute to a CFML tag (any tag that starts with CF, such as <cfform>). TIP If users bookmark one of your pages, the cfid and cftoken information should be part of the bookmarked URL, so that their client variables aren't lost even if their browsers don't support cookies. However, if they just type your site's URL into their browsers directly, it's unlikely that they will include the cfid and cftoken. ColdFusion will be forced to consider them as new visitors, which in turn means that the prior visit's client variables will be lost. ColdFusion will eventually purge the lost session (see the section "Adjusting How Long Client Variables Are Kept," above). NOTE In addition to the cfid, cftoken, and urlToken properties mentioned here, several other automatically maintained properties of the CLIENT scope are available, including hitCout, lastVisit, and timeCreated.
Storing Complex Data Types in Client Variables
As mentioned earlier, you can store only simple values (strings, numbers, dates, and Boolean values) in the CLIENT scope. If you attempt to store one of ColdFusion's complex data types (structures, arrays, queries, and object references) as a client variable, you get an error message. You can, however, use the <cfwddx> tag to transform a complex value into an XML-based string. In this serialized form, the value can be stored as a client variable. Later, when you want to use the variable, you can use <cfwddx> again to transform it from the string format back into its complex form. There isn't space here to discuss the <cfwddx> tag fully, but the following code snippets will be enough to get you started. For more information about <cfwddx>, see Appendix B. For more information about the WDDX technology in general and how it can be used to do much more than this, consult our companion volume, Advanced Macromedia ColdFusion MX 7 Application Development, or visit http://www.openwddx.org. Assuming, for instance, that myStruct is a structure, the following would store it in the CLIENT scope: <cfwddx action="CFML2WDDX" input="#myStruct#" output="CLIENT.myStructAsWddx">
Later, to retrieve the value, you could use the following: <cfwddx action="WDDX2CFML" input="#CLIENT.myStructAsWddx#" output="myStruct">
You then could refer to the values in myStruct normally in your code. If you made any changes to the structure, you would need to store it anew using the first snippet. NOTE You can use the isSimpleValue() function to test whether a value can be stored in the CLIENT scope without using this WDDX technique. NOTE You can use the isWDDX() function to test whether a client variable actually contains a valid WDDX value.
|