In this exercise, we are going to use everything we have learned throughout the book to create simple shopping cart functionality for our Beelze-Bubba site. We might even pick up a couple of new tricks along the way. As the saying goes, there are many ways to skin a cat. There are also many ways to build a shopping cart. The major trait of a shopping cart is the capability to maintain state (or remember what is in the cart) over some period of time. As we have previously discussed, there are several ways to maintain state. The following are several options we could use to store our shopping cart information. Session scope storage. We could store our shopping cart information in a Session scope variable. By using a combination of arrays and the Session scope, we can create a complex data structure for our shopping cart that will be available to any page the user visits. When the customer leaves the site or the session variables time out, the shopping cart will simply be erased. Client scope storage. Although we haven't touched on the Client scope too much, we could also use this scope in combination with client cookies to store our shopping cart information. Using client cookies, we could opt to have the shopping cart persist over multiple visits. The Client scope, however, can only be used to store simple values, and we will not be able to use arrays or structures. Database. We could also create extra tables in our database to store the information contained in our shopping cart. This is probably the most flexible approach. Because the information is stored in a database, it is available to us for recordkeeping or queries regarding sales. However, there also needs to be some procedure in place for archiving old shopping cart information to keep our database from growing to an unmanageable size. Because this is a ColdFusion book and not a database bookand because we just learned about arrayswe are going to adopt the Session scope approach. We will use an array to track the items that the customer has placed in his cart. We will store this array in the customer's Session scope to keep it separate from all other customers'. In addition, by using the Session scope, our shopping cart information will be available on any page that our customer visits. Cart Architecture Before we start to think about coding our shopping cart, we should analyze what we want the cart to be able to do and design how it is going to be put together. When designing a shopping cart, the "keep it simple" approach is probably best. Shopping carts are quite common on the Internet these days, and customers have become accustomed to certain functions and features such as the capability to remove items and to change the quantity of items ordered. To keep customers happy, we want to make the cart as self-explanatory as possible and give them what they expect. Table 10.3 outlines the functions we want our shopping cart to have. Table 10.3. Shopping Cart Features Function | Description |
---|
Add item | The capability to add items from the catalog to the customer's shopping cart. | Remove item | The capability to remove unwanted items from the cart. | Edit quantity | The capability to change the quantity of a given item. | Update items | Update the item(s) with the new quantity value. | Display items | Display the items currently in the cart. | Clear cart | Empty the shopping cart of all items. | We could build six different templates (one to handle each function), but we are going to try something a bit different. If we were to create separate templates for each function, that would be six templates to maintain, and it would also be very easy for those six templates to get out of synch once we begin to change and update our code. Instead of building separate templates for each function, we are going to incorporate all the functionality in Table 10.3 into a single template. This technique is used in some development methodologies, such as Fusebox. To achieve this, we are going to have to let the template know which action it is supposed to be performing at any given time. To accomplish this, we will pass an Action variable to the template whenever is it requested. The template itself will use a SWITCH/CASE statement to determine which code will run. The following code shows how this can be accomplished. <CFSWITCH EXPRESSION="#URL.Action#"> <CFCASE VALUE="Display"> <!--- code to display the cart---> </CFCASE> <CFCASE VALUE="Add"> <!--- code to add an item---> </CFCASE> <CFCASE VALUE="Remove"> <!--- code to remove an item---> </CFCASE> <CFCASE VALUE="Edit"> <!--- code to edit item quantities---> </CFCASE> <CFCASE VALUE="Update"> <!--- code to update array with new quantities---> </CFCASE> <CFCASE VALUE="Clear"> <!--- code to empty the cart---> </CFCASE> </CFSWITCH> By passing a URL scope variable such as Cart.cfm?Action=Add with the page request, we can control which portion of the code gets executed. This method gives us a single template to maintain and troubleshoot for all our cart functions. Strictly speaking, the <CFCASE> blocks can appear in any order. For performance reasons, however, it is wise to try and optimize the order in which they appear. ColdFusion will look through all the <CFCASE> statements, looking for a match for the <CFSWITCH> EXPRESSION value. When it finds a match, it stops looking any further. Consequently, we can save ColdFusion some work if we place the CASE we think will occur the most frequently at the top of the list. Then we place what we anticipate to be the next-most-frequent CASE second on the list, and so on. Data Storage We will be using a two-dimensional array to track items in our cart. Each row in the array will represent a chosen item and will store four elements: product number, product name, quantity, and unit price. Table 10.4 illustrates how the items will be stored in the array. Table 10.4. Shopping Cart Array | col[1] | col[2] | col[3] | col[4] |
---|
row[1] | Product number | Product name | Quantity | Unit price | row[2] | Product number | Product name | Quantity | Unit price | We will store this array in the Session scope for each individual customer. Remember that whenever we access a Session scope variable, it should be locked using the <CFLOCK> tag. For more information on Session scope variables and locking, see Step 8. Figure 10.14 shows what our finished shopping cart will look like. Figure 10.14. The Beelze-Bubba shopping cart. Building the Shopping Cart As we build the shopping cart, we will examine the code segments for each <CFCASE> block individually. At the end of the exercise, we will tie together all these elements to create a fully functioning shopping cart. This shopping cart will use a template called Cart.cfm. Before we can begin to use an array, we have to create it. At the top of our Cart.cfm template, we will include the following code: <!--- check to see if the array exists, if not create it ---> <CFIF NOT IsDefined("SESSION.aCart")> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="EXCLUSIVE" SCOPE="SESSION"> <CFSET SESSION.aCart=ArrayNew(2)> </CFLOCK> </CFIF> <!--- set a default URL.Action variable if none exists ---> <CFPARAM NAME="URL.Action" DEFAULT="Display"> This code uses an IF statement to check for the existence of a SESSION variable called aCart. If the variable does not exist, we use the ArrayNew(2) function to create a new two-dimensional array and then assign it to a SESSION variable. Note that we are using an "a" prefix to label our aCart variable as an array. Because our template functionality all hinges on the use of a URL.Action variable, we will use the <CFPARAM> tag to check for the existence of one. If it does not exist, we will create it and give it the default value of Display. Adding an Item Now that we know our aCart array will exist, let's take a look at how we will add items to our cart. Customers will be able to add items to their cart by clicking on the Add to Cart button on either the ProductList.cfm page or the ProductDetail.cfm page, as shown in Figure 10.15. Figure 10.15. The Add to Cart link. On the ProductList.cfm page, we will use the same JavaScript-based link around the Add to Cart button that we used on the Details button, except this link will call our Cart.cfm page using an Action variable of Add. We also need to pass the ProductNo variable using URL.pid so that we know which product we are adding. Our Cart.cfm page will eventually live in a new NewSite\Cart folder, so the link will look like this one: [View full width] <A HREF="##" onClick="window.open('../Cart/Cart.cfm?action=add&pid=#ProductNo#','window', config='height=400,width=480,toolbar=no,location=no,resizable=1,scrollbars=yes')"> <IMG src="/books/4/16/1/html/2/../images/add_cart.gif" ALT="add the item to your shopping cart" WIDTH="75" HEIGHT="25" BORDER="0"> </A> -
Open the ProductList.cfm page. Find the add_cart.gif image link and surround it with the new link, as shown in the preceding code. Remember to keep the JavaScript all on one line. This link will pop up a second, smaller window just like the link on the Details button. Next we want to create the link on the ProductDetail.cfm template. This time, the small pop-up window will already be open, so we do not need to open another one. -
Open the ProductDetail.cfm page and find the cart_symbol.gif image tag. Add the following link: <A HREF="../Cart/Cart.cfm?action=add&pid=#ProductNo#"> <IMG src="/books/4/16/1/html/2/../images/cart_symbol.gif" WIDTH="150" HEIGHT="25" ALT="add item to your shopping cart" BORDER="0"> </A> Now that we have the links to add items to the shopping cart, let's look at how we are actually going to do it. When a customer clicks one of the Add to Cart links, he will pass two URL name/value pairs along with the page request. One name/value pair will be Action=Add, which nominates which <CFCASE> block to execute on our Cart.cfm page. The other pair will be pid=#ProductNo#, which identifies which item is to be added to the cart. After we have identified the item to be added, we will run a query to retrieve the product details for that item. Then we will use <CFSET> statements to add the product details to the end of the array. After we have added the item to the array, we will display the shopping cart to the customer. Let's have a look at the <CFCASE> block for the Add action. <!--- add an item---> <CFCASE VALUE="Add"> <!--- run query to get details of the item to be added ---> <CFQUERY NAME="qProductDetail" DATASOURCE="#BBWebAppDSN#"> SELECT * FROM Products WHERE ProductNo = '#URL.pid#' </CFQUERY> <!--- determine current length of the array and add new item to the end, assume a quantity of one it can be changed later---> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="EXCLUSIVE" SCOPE="SESSION"> <CFSET LastRow=ArrayLen(SESSION.aCart)> <CFSET SESSION.aCart[LastRow+1][1]=qProductDetail.ProductNo> <CFSET SESSION.aCart[LastRow+1][2]=qProductDetail.Name> <CFSET SESSION.aCart[LastRow+1][3]="1"> <!--- see if the product is on special and use special price ---> <CFIF qProductDetail.OnSpecial IS NOT 0> <CFSET SESSION.aCart[LastRow+1][4]=qProductDetail.SpecialPrice> <CFELSE> <CFSET SESSION.aCart[LastRow+1][4]=qProductDetail.Price> </CFIF> </CFLOCK> <CFLOCATION URL="Cart.cfm?Acion=Display" ADDTOKEN="No"> </CFCASE> In the preceding code, we first run a query based on the URL.pid value passed from the link. After we have retrieved the appropriate product details, we begin the process of adding the information to the array. First we use the ArrayLen() function to determine the current length of the array. Next we use a series of <CFSET> statements to add a row of product information to the end of the array. At this point, we assume a quantity of one; we will give the customer the capability to change quantities in just a moment. We then use an IF statement to see if the product is on special to determine which unit price to add to the array. Finally, we use the <CFLOCATION> tag to request the same page, but this time we use the Display action to instruct the template to display the shopping cart. Removing an Item To remove a particular item from the array, we need to know its location (or, in this case, row number). The Remove Item link shown in Figure 10.14 passes a link like the following, where x is the row number of the item to be removed. <A HREF="Cart.cfm?Action=Remove&Row=#x#">remove item</A> When we know the location (or row number) of the item to be removed, we can simply use the ArrayDeleteAt() function to get rid of that row. All the other rows with higher row numbers will reshuffle to fill in the empty space. The code for the <CFCASE Value="Remove"> block is as follows: <CFCASE VALUE="Remove"> <!--- remove row number passed via URL---> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="EXCLUSIVE" SCOPE="SESSION"> <CFSET RemoveRow=ArrayDeleteAt(SESSION.aCart,URL.Row)> </CFLOCK> <CFLOCATION URL="Cart.cfm?Action=Display" ADDTOKEN="No"> </CFCASE> This code simply removes the row passed by the URL.Row variable and then displays the shopping cart again. Editing Quantity Values We also need to give our customers the capability to change the quantity values for the products they have ordered. This will be a two-step process. First, we will display a form within the customer's shopping cart. This form will contain an input box that will be populated with the current quantity value. The customer can then enter the new quantity values for all products at the same time. This form will be displayed when the page is requested with the URL variable Action=Edit. When the customer submits the form using the Update button, the form will submit itself with a new URL.Action value. This new Action value will trigger the code to process the form. Let's have a look at the <CFCASE> block for the Edit action. <!--- edit item quantities ---> <CFCASE VALUE="Edit"> <FORM ACTION="Cart.cfm?Action=Update" METHOD="POST"> <TABLE WIDTH="450" BORDER="0" CELLSPACING="0" CELLPADDING="3" ALIGN="Center"> <TR> <TD COLSPAN="5"> Change the quantities of any items and click the Update button. </TD> </TR> <TR BGCOLOR="#FFD700"> <TD >SKU</TD> <TD >Name</TD> <TD ALIGN="center" >Qty</TD> <TD ALIGN="right" >Price</TD> <TD ALIGN="right" >Total</TD> </TR> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="READONLY" SCOPE="SESSION"> <CFLOOP INDEX="x" FROM="1" TO="#ArrayLen(SESSION.aCart)#"> <CFOUTPUT> <CFIF x MOD 2 IS 0> <!--- even row color ---> <TR BGCOLOR="##E8E8E8"> <CFELSE> <!--- odd row color ---> <TR BGCOLOR="##FFFFFF"> </CFIF> <TD>#UCase(SESSION.aCart[x][1])#</TD> <TD>#SESSION.aCart[x][2]#</TD> <!--- put input box here, populate with current qty ---> <TD ALIGN="center"> <INPUT TYPE="text" NAME="#x#" VALUE="#SESSION.aCart[x][3]#" SIZE="3" MAXLENGTH="4"> </TD> <TD ALIGN="right">#DollarFormat(SESSION.aCart[x][4])#</TD> <TD ALIGN="right">#DollarFormat(SESSION.aCart[x][3] * SESSION.aCart[x][4])#</TD> </TR> </CFOUTPUT> </CFLOOP> </CFLOCK> <TR> <TD> </TD> <TD> </TD> <TD> </TD> <TD><B>Update Total</B></TD> <TD ALIGN="right"><INPUT TYPE="submit" VALUE="Update"></TD> </TR> </TABLE> </FORM> </CFCASE> We begin by creating a form that posts its information back to the Cart.cfm page using an Action=Update URL variable. We then create a table to display the current shopping cart information. We use the <CFLOOP> tag to loop through all the products in the array. When we get to the quantity information, we display an input box populated with that product's current quantity value. The NAME attribute of each form field will be the current row value of the loop. By doing this, we will end up with form fields called FORM.1, FORM.2, FORM.3, and so on. These numbers will directly correspond to the row number of the product they represent. Finally, we display an Update button that, when clicked, submits the form. Values entered in the quantity boxes will be validated by the code that processes the form information. Updating Quantity Values When the code from the Edit section is submitted, it sends the form information back to the Cart.cfm page along with the Action=Update URL variable. The <CFFCASE> for the Update action checks to make sure the form fields do not contain any bogus information. Then, using <CFLOOP>, we loop through the array, replacing the old quantity information with the new information. Let's have a look. <CFCASE VALUE="Update"> <!--- make sure all form fields contain a value of at least 1 ---> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="READONLY" SCOPE="SESSION"> <CFLOOP INDEX="x" FROM="1" TO="#ArrayLen(SESSION.aCart)#"> <!--- check for blank values and set to 1---> <CFIF Form[x] IS ""> <CFSET Form[x]=1> </CFIF> <!--- check for values less than 1 and reset to 1 ---> <CFIF Form[x] LT 1> <CFSET Form[x]=1> </CFIF> <!--- check for non number values and reset to 1 ---> <CFIF NOT IsNumeric(Form[x])> <CFSET Form[x]=1> </CFIF> </CFLOOP> </CFLOCK> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="READONLY" SCOPE="SESSION"> <CFLOOP INDEX="x" FROM="1" TO="#ArrayLen(SESSION.aCart)#"> <!--- update new value, round if decimal is present---> <CFSET SESSION.aCart[x][3]=#Round(FORM[x])#> </CFLOOP> </CFLOCK> <!--- display new totals ---> <CFLOCATION URL="Cart.cfm?Action=Display" ADDTOKEN="No"> </CFCASE> We start by creating a loop in order to loop through all the products contained in our shopping cart. We use a few IF statements to check the validity of the new quantity information. We are referring to the form fields by their position on the submitted form: Form[1], Form[2], and so on. We do this by using the x index value from our loop. The validity checks make sure no funny business is going on with the quantity information. If a submitted quantity field is blank, we reset it to a value of 1. If the value is less than 1 (0 or a negative number), we reset the value to 1. If someone enters non-numeric information such as letters or symbols, we reset the value to 1. In addition, when we actually go to set the new quantity value, we round off any decimal places using the Round() function, just in case someone is trying to be funny and order 1.23 cases of something. We use a simple <CSET> statement to set element [3] to the new quantity value from the form. After the loop is complete, we use a <CFLOCATION> tag to request the Cart.cfm page again, this time with an Action=Display URL variable. Clearing the Cart We would also like to give our customers the capability to empty their shopping cart so they can start over or just get rid of the contents. This is a pretty straightforward affair. We simply have to use the ArrayClear() function to wipe out the contents of the array. The array will still exist, so we do not have to re-create it when the customer wants to start shopping again. The code block for clearing the array is as follows: <!--- empty the cart ---> <CFCASE VALUE="Clear"> <!--- clear the array ---> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="EXCLUSIVE" SCOPE="SESSION"> <CFSET DumpCart=ArrayClear(SESSION.aCart)> </CFLOCK> <CFLOCATION URL="Cart.cfm?Action=Display" ADDTOKEN="No"> </CFCASE> Displaying the Cart Finally, we come to the code portion for actually displaying our shopping cart. Nothing too cosmic is going on here. We start off with an IF statement to determine whether the cart currently has any items in it or is empty. If the cart/array is currently empty, we display a message telling the customer how to add items to the cart and display a link for closing the pop-up window. If there are items currently stored in the cart, we then create a table to display contents of our array. We create a header row, followed by a loop, to loop through all the items in the array. We use a simple mathematical expression to display a subtotal for each item. We also create and use a local variable called CartTotal to create a running total for the shopping cart. Finally, we display a menu bar with cart options that enable the customer to close the window, change quantities, empty the cart, and proceed to the checkout. [View full width] <!--- display the contents of the cart ---> <CFCASE VALUE="Display"> <!--- check to see if there are any items in the cart ---> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="READONLY" SCOPE="SESSION"> <CFIF ArrayLen(SESSION.aCart) GT 0> <TABLE WIDTH="450" BORDER="0" CELLSPACING="0" CELLPADDING="3" ALIGN="Center"> <TR> <TD COLSPAN="5"> The current contents of your shopping cart are listed below. You can use the Change Quantities button to amend the quantity of all items. </TD> </TR> <TR BGCOLOR="#FFD700"> <TD >SKU</TD> <TD >Name</TD> <TD ALIGN="center" >Qty</TD> <TD ALIGN="right" >Price</TD> <TD ALIGN="right" >Total</TD> </TR> <CFLOOP INDEX="x" FROM="1" TO="#ArrayLen(SESSION.aCart)#"> <CFOUTPUT> <CFIF x MOD 2 IS 0> <!--- even row color ---> <TR BGCOLOR="##E8E8E8"> <CFELSE> <!--- odd row color ---> <TR BGCOLOR="##FFFFFF"> </CFIF> <TD VALIGN="top">#UCase(SESSION.aCart[x][1])#</TD> <TD> #SESSION.aCart[x][2]# <BR> <FONT SIZE="1"> <A HREF="Cart.cfm?Action=Remove&Row=#x#">remove item</A> </FONT> </TD> <TD ALIGN="center">#SESSION.aCart[x][3]#</TD> <TD ALIGN="right">#DollarFormat(SESSION.aCart[x][4])#</TD> <TD ALIGN="right">#DollarFormat(SESSION.aCart[x][3] SESSION.aCart[x][4])#</ TD> </TR> </CFOUTPUT> <!--- calculate cart total ---> <CFPARAM NAME="CartTotal" DEFAULT="0"> <CFSET ItemTotal= SESSION.aCart[x][3] * SESSION.aCart[x][4]> <CFSET CartTotal=CartTotal + ItemTotal> </CFLOOP> <TR> <TD> </TD> <TD> </TD> <TD> </TD> <TD><B>Cart Total</B></TD> <TD ALIGN="right"><CFOUTPUT><B>#DollarFormat(CartTotal)#</B></CFOUTPUT></TD> </TR> </TABLE> <!--- cart options ---> <TABLE WIDTH="450" ALIGN="Center" CELLSPACING="0" CELLPADDING="3"> <TR> <TD COLSPAN="4"> <B>Cart Options</B> </TD> </TR> <TR> <TD><A HREF="##" onClick="self.close()">Continue To Shop</A></TD> <TD><A HREF="Cart.cfm?Action=Edit">Change Quantities</A></TD> <TD><A HREF="Cart.cfm?Action=Clear">Empty Cart</A></TD> <TD><A HREF="CartAction.cfm">Check Out</A></TD> </TR> </TABLE> <CFELSE> <DIV ALIGN="center"> <P>Your cart is currently empty. <BR> Please browse through our products and add some items to your cart.</P> <P><A HREF="##" onClick="self.close()">Continue To Shop</A></P> </DIV> </CFIF> </CFLOCK> </CFCASE> Putting It All Together If we put it all together, we have the code displayed in Listing 10.8. You can find the completed file in the \CompletedFiles\MakeoverExercises\Step10\Cart folder. Listing 10.8 Code For Cart.cfm [View full width] <!--- File: Cart.cfm Description: Manages shopping cart session array Author: Created: ---> <!--- Cart Architecture We will use a two dimensional array to keep track of items in the cart. We will store the array in a Session scope variable so that it is available for the duration of the customer's session Each row in the array will hold four elements as outlined below 1 - ProductNo 2 - ProductName 3 - Quantity 4 - Unit Price Don't forget all Session scope variables should be locked before use ---> <!--- check to see if the array exists, if not create it ---> <CFIF NOT IsDefined("SESSION.aCart")> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="EXCLUSIVE" SCOPE="SESSION"> <CFSET SESSION.aCart=ArrayNew(2)> </CFLOCK> </CFIF> <!--- set a default URL.Action variable if none exists ---> <CFPARAM NAME="URL.Action" DEFAULT="Display"> <HTML> <HEAD> <TITLE>Beelze-Bubba Shopping Cart</TITLE> <LINK REL="stylesheet" HREF="../styles/bbstyle.css" TYPE="text/css"> </HEAD> <BODY BGCOLOR="#FFFFFF" TEXT="#000000" LEFTMARGIN="0" TOPMARGIN="0" MARGINWIDTH="0" MARGINHEIGHT="0" onLoad="self.focus()"> <!--- header table ---> <TABLE WIDTH="450" ALIGN="Center" BGCOLOR="#CC0000" CELLSPACING="0" CELLPADDING="3"> <TR> <TD > Beelze-Bubba Shopping Cart </TD> </TR> </TABLE> <BR> <!--- use the URL.Action variable to determine what action to perform ---> <CFSWITCH EXPRESSION="#URL.Action#"> <!--- display the contents of the cart ---> <CFCASE VALUE="Display"> <!--- check to see if there are any items in the cart ---> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="READONLY" SCOPE="SESSION"> <CFIF ArrayLen(SESSION.aCart) GT 0> <TABLE WIDTH="450" BORDER="0" CELLSPACING="0" CELLPADDING="3" ALIGN="Center"> <TR> <TD COLSPAN="5"> The current contents of your shopping cart are listed below. You can use the Change Quantities button to amend the quantity of all items. </TD> </TR> <TR BGCOLOR="#FFD700"> <TD >SKU</TD> <TD >Name</TD> <TD ALIGN="center" >Qty</TD> <TD ALIGN="right" >Price</TD> <TD ALIGN="right" >Total</TD> </TR> <CFLOOP INDEX="x" FROM="1" TO="#ArrayLen(SESSION.aCart)#"> <CFOUTPUT> <CFIF x MOD 2 IS 0> <!--- even row color ---> <TR BGCOLOR="##E8E8E8"> <CFELSE> <!--- odd row color ---> <TR BGCOLOR="##FFFFFF"> </CFIF> <TD VALIGN="top">#UCase(SESSION.aCart[x][1])#</TD> <TD> #SESSION.aCart[x][2]# <BR> <FONT SIZE="1"><A HREF="Cart. cfm?Action=Remove&Row=#x#">remove item</A></FONT> </TD> <TD ALIGN="center">#SESSION.aCart[x][3]#</TD> <TD ALIGN="right">#DollarFormat(SESSION.aCart[x][4])#</TD> <TD ALIGN="right">#DollarFormat(SESSION.aCart[x][3] * SESSION.aCart[x][4])#</TD> </TR> </CFOUTPUT> <!--- calculate cart total ---> <CFPARAM NAME="CartTotal" DEFAULT="0"> <CFSET ItemTotal= SESSION.aCart[x][3] * SESSION.aCart[x][4]> <CFSET CartTotal=CartTotal + ItemTotal> </CFLOOP> <TR> <TD> </TD> <TD> </TD> <TD> </TD> <TD><B>Cart Total</B></TD> <TD ALIGN="right"><CFOUTPUT><B>#DollarFormat(CartTotal)#</B></CFOUTPUT></TD> </TR> </TABLE> <!--- cart options ---> <TABLE WIDTH="450" ALIGN="Center" CELLSPACING="0" CELLPADDING="3"> <TR> <TD COLSPAN="4"> <B>Cart Options</B> </TD> </TR> <TR> <TD><A HREF="##" onClick="self.close()">Continue To Shop</ A></TD> <TD><A HREF="Cart.cfm?Action=Edit">Change Quantities</A></ TD> <TD><A HREF="Cart.cfm?Action=Clear">Empty Cart</A></TD> <TD><A HREF="CartAction.cfm">Check Out</A></TD> </TR> </TABLE> <CFELSE> <DIV ALIGN="center"> <P>Your cart is currently empty. <BR> Please browse through our products and add some items to your cart.</P> <P><A HREF="##" onClick="self.close()">Continue To Shop</A></P> </DIV> </CFIF> </CFLOCK> </CFCASE> <!--- add an item---> <CFCASE VALUE="Add"> <!--- run query to get details of the item to be added ---> <CFQUERY NAME="qProductDetail" DATASOURCE="#BBWebAppDSN#"> SELECT * FROM Products WHERE ProductNo = '#URL.pid#' </CFQUERY> <!--- determine current length of the array and add new item to the end assume a quantity of one, it can be changed later---> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="EXCLUSIVE" SCOPE="SESSION"> <CFSET LastRow=ArrayLen(SESSION.aCart)> <CFSET SESSION.aCart[LastRow+1][1]=qProductDetail.ProductNo> <CFSET SESSION.aCart[LastRow+1][2]=qProductDetail.Name> <CFSET SESSION.aCart[LastRow+1][3]="1"> <!--- see if this product is on special and use special price ---> <CFIF qProductDetail.OnSpecial IS NOT 0> <CFSET SESSION.aCart[LastRow+1][4]=qProductDetail. SpecialPrice> <CFELSE> <CFSET SESSION.aCart[LastRow+1][4]=qProductDetail.Price> </CFIF> </CFLOCK> <CFLOCATION URL="Cart.cfm?Acion=Display" ADDTOKEN="No"> </CFCASE> <!--- remove a selected item ---> <CFCASE VALUE="Remove"> <!--- remove row number passed via URL---> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="EXCLUSIVE" SCOPE="SESSION"> <CFSET RemoveRow=ArrayDeleteAt(SESSION.aCart,URL.Row)> </CFLOCK> <CFLOCATION URL="Cart.cfm?Action=Display" ADDTOKEN="No"> </CFCASE> <!--- edit item quantities ---> <CFCASE VALUE="Edit"> <FORM ACTION="Cart.cfm?Action=Update" METHOD="POST"> <TABLE WIDTH="450" BORDER="0" CELLSPACING="0" CELLPADDING="3" ALIGN="Center"> <TR> <TD COLSPAN="5"> Change the quantities of any items and click the Update button. </TD> </TR> <TR BGCOLOR="#FFD700"> <TD >SKU</TD> <TD >Name</TD> <TD ALIGN="center" >Qty</TD> <TD ALIGN="right" >Price</TD> <TD ALIGN="right" >Total</TD> </TR> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="READONLY" SCOPE="SESSION"> <CFLOOP INDEX="x" FROM="1" TO="#ArrayLen(SESSION.aCart)#"> <CFOUTPUT> <CFIF x MOD 2 IS 0> <!--- even row color ---> <TR BGCOLOR="##E8E8E8"> <CFELSE> <!--- odd row color ---> <TR BGCOLOR="##FFFFFF"> </CFIF> <TD>#UCase(SESSION.aCart[x][1])#</TD> <TD>#SESSION.aCart[x][2]#</TD> <!--- put input box here, populate with current qty ---> <TD ALIGN="center"> <INPUT TYPE="text" NAME="#x#" VALUE="#SESSION.aCart[x][3]#" SIZE="3" MAXLENGTH="4"> </TD> <TD ALIGN="right">#DollarFormat(SESSION.aCart[x][4])#</ TD> <TD ALIGN="right">#DollarFormat(SESSION.aCart[x][3] * SESSION.aCart[x][4])#</TD> </TR> </CFOUTPUT> </CFLOOP> </CFLOCK> <TR> <TD> </TD> <TD> </TD> <TD> </TD> <TD><B>Update Total</B></TD> <TD ALIGN="right"><INPUT TYPE="submit" VALUE="Update"></TD> </TR> </TABLE> </FORM> </CFCASE> <!--- use array insert at to update qtys ---> <CFCASE VALUE="Update"> <!--- make sure all form fields contain a value of at least 1 ---> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="READONLY" SCOPE="SESSION"> <CFLOOP INDEX="x" FROM="1" TO="#ArrayLen(SESSION.aCart)#"> <!--- check for blank values and set to 1---> <CFIF Form[x] IS ""> <CFSET Form[x]=1> </CFIF> <!--- check for values less than 1 and reset to 1 ---> <CFIF Form[x] LT 1> <CFSET Form[x]=1> </CFIF> <!--- check for non number values and reset to 1 ---> <CFIF NOT IsNumeric(Form[x])> <CFSET Form[x]=1> </CFIF> </CFLOOP> </CFLOCK> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="READONLY" SCOPE="SESSION"> <CFLOOP INDEX="x" FROM="1" TO="#ArrayLen(SESSION.aCart)#"> <!--- update new value, round if decimal is present---> <CFSET SESSION.aCart[x][3]=#Round(FORM[x])#> </CFLOOP> </CFLOCK> <!--- display new totals ---> <CFLOCATION URL="Cart.cfm?Action=Display" ADDTOKEN="No"> </CFCASE> <!--- empty the cart ---> <CFCASE VALUE="Clear"> <!--- clear the array ---> <CFLOCK TIMEOUT="10" THROWONTIMEOUT="No" TYPE="EXCLUSIVE" SCOPE="SESSION"> <CFSET DumpCart=ArrayClear(SESSION.aCart)> </CFLOCK> <CFLOCATION URL="Cart.cfm?Action=Display" ADDTOKEN="No"> </CFCASE> </CFSWITCH> </BODY> </HTML> Checking Out What happens when the customer clicks the Check Out link? There are myriad possibilities for what exists behind the Check Out link. Typically we would, at a minimum, need to gather contact details for our customer. We would also need some way of processing the order. This could be as simple as emailing the information to the sales department or as complex as interfacing with some legacy stock-management system. Additionally, you might want some way of handling online payment transactions. This is typically accomplished by integrating with third-party payment processors or banks. For links to some popular online payment processors, visit the www.LearnColdFusionMX.com web site. If you would simply like to test the functionality of the Check Out link, you can use the CartAction.cfm template. You will find the CartAction.cfm template in the CompletedFiles\MakeoverExercises\Step10\Cart folder. Simply enter values for your email address and SMTP server. This template will email the contents of the shopping cart to the nominated address in HTML format and then empty the cart. Well, that's it. The last makeover exercise is complete. Congratulations! You now have a fully functioning, dynamic, data-driven web application. I hope you have enjoyed the journey. For more tips and tutorials, visit the www.LearnColdFusionMX.com web site. |