The MusicMad Shopping
Basket (Part 1)
Page 8 of 22 - Chapter
14
The
MusicMad shopping basket relies exclusively on cookies, in particular the
basket cookie which we set. Given we have server-side programming available to
us you may wonder why we still use cookies stored on the client machine for
storing our basket's contents instead of say, session variables or the
database.
The
main reason is scalability. Storing any data on the server consumes the finite
server resources available. On a popular web site at peak times user sessions
could run into hundreds of thousands, maybe more. Session variables consume
resources until the user's session times out, which is 20 minutes by default.
Using a database to store basket contents is a possibility but again your
database could grow very large, even if just temporarily. It also requires
database connection each time a user accesses their basket which, again, is
costly in terms of server memory and processing time. It's likely the user's
machine has more than sufficient resources to cope with our shopping basket's
storage and processing, so it makes sense to distribute responsibility here.
Adding Items to the Basket
align="left">When
we wrote the code which dynamically creates a page with a list of products, we
also put an <A>
tag for each item with Add
to Basket as the link's text and AddToBasket.asp
as its href
property. We also appended ItemId,
the number of CDs wanted (by default 1),
ArtistName,
record Title
and Price
per item to the end of the <A> tag's href.
We can now retrieve those values in our server-side ASP script.
The
values passed in the URL were also escaped
into a URL-encoded form. Effectively, this means that non-alphabetic
characters were converted to a hexadecimal format. For example, a space would
become %20
if there were any in the link. So, the first thing we must do is unescape
those values and split them up into the individual data values for ItemId,
ArtistName
etc. We use the ItemId
to check there is not already one of those items already in the basket.
If there is then we get the current quantity in the basket and add one to it.
Finally, we use the setItemCookie
function that's been made available by including our server-side global module
at the top of the page. It's perhaps worth mentioning here that server-side
include directives cannot have include
directives in them to other files, precluding the errors inherent in an
'include chain'. This is managed as ASP has a one-step preprocessing stage by
any server-side code is executed and the include
statement is dealt with in this stage.
<!--#include
file="ServerSideGlobalDef.inc"-->
<%
// decode query string from HTTP form
var sQueryString = unescape(Request.QueryString);
var sBasketCookie = new
String(Request.Cookies("Basket"));
// String is in form
// IDItemId&Qty&ArtistName&Description&Price
var NewItemData = sQueryString.split("&");
if (sBasketCookie.indexOf(NewItemData[0] +
"&") >= 0 )
{
var ExistItemData =
getItemFromCookie(NewItemData[0],sBasketCookie);
NewItemData[1]
=parseInt(NewItemData[1])+parseInt(ExistItemData[1]);
}
Response.Cookies("Basket")=setItemToCookie(NewItemData,sBasketCookie);
%> |
We
complete the page by adding a message informing the user what has just been
successfully added to their basket.
It's
also nice, from a user interface point of view, to give them the option to
view their basket or proceed to the checkout by putting a form with two
buttons on the page. The event handler code has been added to the HTML tags
and simply replaces the current page of the main frame with the relevant
pages for viewing the basket, which we create next, and the checkout page
which will be created later.
<HTML>
<BODY>
<BR><BR><BR>
<P align="center">
<FONT FACE="Comic Sans MS"
SIZE="3">
1 copy of <STRONG><%=
NewItemData[3] %></STRONG><BR>
by <STRONG><%=
NewItemData[2] %></STRONG>
has been added to your shopping
basket
</FONT>
</P>
<DIV align="center">
<FORM ACTION="" method="POST">
<INPUT TYPE="Button" NAME="cmdCheckout"
VALUE="Proceed to Checkout" _
onClick="window.location.replace('checkout_frame.php')">
<INPUT TYPE="Button" NAME="cmdBasket"
VALUE="View Basket" _
onClick="window.location.replace('viewbasket.asp')">
</FORM>
</DIV>
</BODY>
</HTML> |
Save the page as AddToBasket.asp
Viewing the Basket's Contents
align="left">The
screenshot above shows what we are aiming for as regards the contents and
layout of the basket. As well as showing each item, the quantity, price and
total price we also have a summary of the order cost at the end.
align="left">Before
we create the page that views the basket, we are going to create another
server-side include file: Basket.inc.
As you will see later, viewing the contents of the basket is something we need
to do in a number of different places on the site, so by creating an include
file we keep the code in one place making debugging and maintenance easier.
align="left">The
basket include file retrieves each item from the basket cookie used to store
the goods and produces a table listing the item's artist, description,
quantity selected, price per item and cost of the total quantity of that item
in the basket. Finally at the end of the table it produces a sub total of the
cost of all the items, cost of delivery and final cost.
The
include file uses a variable bReadOnly
which we define and set outside the include file. If bReadOnly
is false
then in each item row in the table, the quantity of each item is displayed
inside a text box. Below the table is a button which when clicked updates the
quantities based on the values in the quantity boxes. If the user wishes to
remove an item from the basket they enter 0
in its quantity text box and hit the Update
Quantities button.
align="left">If
bReadOnly
has been set to true
then quantities are displayed as plain text and there is no Update
Quantities button. This gives us a method of displaying a
non-updateable summary of basket items at checkout.
align="left">Our
first task is to make sure the user is viewing the very latest details and not
a stale cached version of the basket. We can accomplish this by setting the Response.Expires
property to -1,
as we did in browse.asp,
to ensure the page expires as soon as it is loaded by the browser.
align="left">Following
that is a JavaScript form validation function, checkQtys.
This function loops through each element in the basket's form and if the
element is a text box, (i.e. its type property is text)
we check that the contents are a valid whole number. It's worth mentioning
here that the <FORM>
tag is actually written outside the include file as this makes it easier to
set parameters such as the form's action, method, name and events. This makes
our whole code a little more adaptable to different situations. The validation
routine does not need to know the name of the form it's validating because we
pass the form
object itself as the function's parameter.
align="left">If
parsing the text box's value to an integer produces NaN,
we know that the user has entered some invalid value. We react to this with a
warning message, then set focus to the offending element and select the text
inside it (just to make things really clear to the user). Also, false
is returned from the function and is important if this routine is used as the onSubmit
event handler, as returning false
cancels the submit event.
<% Response.Expires
= -1; %>
<SCRIPT LANGUAGE="JavaScript">
function checkQtys(theForm)
{
for (var iElement = 0; iElement <
theForm.length;iElement++)
{
if (theForm[iElement].TYPE ==
"text")
{
if (isNaN(parseInt(theForm[iElement].VALUE))
|| _
(parseInt(theForm[iElement].VALUE) < 0))
{
alert("The quantity you have entered is invalid\n _
Only whole numbers are valid in this box");
theForm[iElement].focus();
theForm[iElement].select();
return false;
}
}
}
return true;
}
</SCRIPT> |
Following the validation
script we define the table and its headers and then start looping through
the basket's contents. As we saw above, each item in the basket cookie is in
the format:
ItemId&Qty&ArtistName&Title&Price£&