Build An AJAX Powered Online Shopping Cart

Build An AJAX Powered Shopping Cart

Aug 21st in PHP by James Lao

The NETTUTS community asked for it. So here you are! One of the innovations of the web is online shopping. It allows us to buy things without ever leaving the comfort of our homes. However, the most basic element of online shopping, the shopping cart, has not evolved much. In this tutorial, we're going to make an AJAX powered shopping cart using PHP, jQuery, and a few nifty plugins.

PG

Author: James Lao

I'm a web developer from Oregon. Currently I'm a student at Carnegie Mellon University majoring in computer science.

Preface

The goal of this tutorial is to show you how to build an AJAX powered shopping cart. However, it will not be production ready. The back end requirements vary from site to site far too much to write an effective tutorial. Instead, we are going to focus on the AJAX parts. The back end code in this tutorial acts as scaffolding for us to build the AJAX functionality, however, it can be built off of to meet your own site's requirements. In the last section, we'll discuss some possible next steps to implementing this in your own websites.

The other thing to note is that this tutorial will not explain all the details. A firm grasp of HTML, CSS, and some basic PHP is expected. By basic I mean variables, arrays, control structures, and basic OOP. Some knowledge of Javascript is a plus. We'll walk through and break down the trickier bits of PHP and Javascript, but will gloss over the basic stuff like CSS styling. Links to documentation and other relevant resources will be sprinkled throughout wherever relevant.

The final thing to note is that the order codes (or product codes) used in this tutorial are completely arbitrary.

The Demo

The demo page shows a few different ways our AJAX shopping cart can function. It should be noted that this is not a production ready shopping cart. Due to variability of the requirements from site to site, this tutorial will only cover building the skeleton while you will have to code in the details for retrieving product names, prices, and other data that might come from a database.

Step 1 - Downloading the scripts

We're going to be using jQuery, the jQuery color animations plugin, and Thickbox. The color plugin extends jQuery to allow us to use jQuery to animate colors and Thickbox lets us create quick and easy modal windows.

Create a directory on your web server for the cart to live in. For this tutorial we'll be using cart/. Substitute cart/ with the directory you are using on your server. Inside the cart/ directory create js/, css/, and images/ folders for storing your Javascript, CSS, and images.

Download the jQuery, color plugin, and Thickbox files and save them into the appropriate folders we just created in the cart/ directory. Make sure you download the uncompressed thickbox.js.

Your folder structure should look something like this. I have renamed some of the files but it should be pretty obvious what each one is.

cart/js/jquery-1.2.6.pack.js
cart/js/jquery.color.js
cart/js/thickbox.js
cart/css/thickbox.css
cart/images/loadingAnimation.gif
cart/images/macFFBgHack.png

Step 2 - Setup Thickbox

Since our folder structure is a little different from the default Thickbox one, we're going to need to fix some of the paths referencing loadingAnimation.gif and macFFBgHack.png.

Open thickbox.js and you'll see the following line of code after the comments (line 8):

var tb_pathToImage = "images/loadingAnimation.gif";

Change that to the following so it references the loadingAnimation.gif file correctly:

var tb_pathToImage = "../images/loadingAnimation.gif";

Next open up thickbox.css and find the line that says (line 37):

.TB_overlayMacFFBGHack {background: url(macFFBgHack.png) repeat;}

And change it to:

.TB_overlayMacFFBGHack {background: url(../images/macFFBgHack.png) repeat;}

Step 3 - The Shopping_Cart class

We will create a class to handle all the different actions such as adding items to the cart and saving the cart. The following is the code for the Shopping_Cart class we'll be using for this tutorial. Again, this is very barebones intentionally because the backend requirements will be different from site to site. Since there isn't much code, I will not explain each and every method and instead let the comments do the explaining. Save this as shopping_cart.class.php. We'll take a look at how to use this class in the next step when we create a sample load script.

<?php

class Shopping_Cart {
	var $cart_name;       // The name of the cart/session variable
	var $items = array(); // The array for storing items in the cart
	
	/**
	 * __construct() - Constructor. This assigns the name of the cart
	 *                 to an instance variable and loads the cart from
	 *                 session.
	 *
	 * @param string $name The name of the cart.
	 */
	function __construct($name) {
		$this->cart_name = $name;
		$this->items = $_SESSION[$this->cart_name];
	}
	
	/**
	 * setItemQuantity() - Set the quantity of an item.
	 *
	 * @param string $order_code The order code of the item.
	 * @param int $quantity The quantity.
	 */
	function setItemQuantity($order_code, $quantity) {
		$this->items[$order_code] = $quantity;
	}
	
	/**
	 * getItemPrice() - Get the price of an item.
	 *
	 * @param string $order_code The order code of the item.
	 * @return int The price.
	 */
	function getItemPrice($order_code) {
		// This is where the code taht retrieves prices
		// goes. We'll just say everything costs $9.99 for this tutorial.
		return 9.99;
	}
	
	/**
	 * getItemName() - Get the name of an item.
	 *
	 * @param string $order_code The order code of the item.
	 */
	function getItemName($order_code) {
		// This is where the code that retrieves product names
		// goes. We'll just return something generic for this tutorial.
		return 'My Product (' . $order_code . ')';
	}
	
	/**
	 * getItems() - Get all items.
	 *
	 * @return array The items.
	 */
	function getItems() {
		return $this->items;
	}
	
	/**
	 * hasItems() - Checks to see if there are items in the cart.
	 *
	 * @return bool True if there are items.
	 */
	function hasItems() {
		return (bool) $this->items;
	}
	
	/**
	 * getItemQuantity() - Get the quantity of an item in the cart.
	 *
	 * @param string $order_code The order code.
	 * @return int The quantity.
	 */
	function getItemQuantity($order_code) {
		return (int) $this->items[$order_code];
	}
	
	/**
	 * clean() - Cleanup the cart contents. If any items have a
	 *           quantity less than one, remove them.
	 */
	function clean() {
		foreach ( $this->items as $order_code=>$quantity ) {
			if ( $quantity < 1 )
				unset($this->items[$order_code]);
		}
	}
	
	/**
	 * save() - Saves the cart to a session variable.
	 */
	function save() {
		$this->clean();
		$_SESSION[$this->cart_name] = $this->items;
	}
}

?>

Step 4 - load.php

Before we do anything else, we're going to create a simple script that loads some sample items into the cart. This will make building the actual cart page easier. Let's name this file load.php and save it in the cart/ directory.

<?php
	include('shopping_cart.class.php');
	session_start();
	$Cart = new Shopping_Cart('shopping_cart');
	
	$Cart->setItemQuantity('HSD-KSE', 2);
	$Cart->setItemQuantity('KLS-IEN', 1);
	$Cart->setItemQuantity('ELS-OWK', 4);
	
	$Cart->save();
	
	header('Location: cart.php');
?>

The first three lines include the shopping cart class we created in the previous step, start the session so we can save the cart, and create a new Shopping_Cart instance. These three lines will be at the top of any file that needs to access the shopping cart. Notice how on line 3 I pass a single parameter, 'shopping_cart', when I create the Shopping_Cart instance. 'shopping_cart' gets passed to the constructor of the class, which sets the instance variable $cart_name. This is the name of the session variable that we will be storing all the cart data. The reason we do this is to avoid conflicts with other carts.

The rest of the code simply adds three items to the cart, saves them, and redirects the user to the cart itself which we'll be building in the next step. Methods (which are basically functions) in a class are accessed using a special arrow syntax.

Step 5 - Building the cart

We're going to build the cart, but without the AJAX functionality first, so that in the event that the user has Javascript disabled, she will still be able to use the cart. This is important, because we want her to buy something and she won't be able to do that if it doesn't degrade well when Javascript is disabled!

<?php
	include('shopping_cart.class.php');
	session_start();
	$Cart = new Shopping_Cart('shopping_cart');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title>Shopping Cart</title>

		<script src="js/jquery-1.2.6.pack.js" type="text/javascript"></script>
		<script src="js/jquery.color.js" type="text/javascript"></script>
		<script src="js/cart.js" type="text/javascript"></script>
		<link href="css/cart.css" rel="stylesheet" type="text/css" media="screen" />
	</head>

	<body>
		<div id="container">
			<h1>Shopping Cart</h1>
			<?php if ( $Cart->hasItems() ) : ?>
			<form action="cart_action.php" method="get">

				<table id="cart">
					<tr>
						<th>Quantity</th>
						<th>Item</th>
						<th>Order Code</th>

						<th>Unit Price</th>
						<th>Total</th>
						<th>Remove</th>
					</tr>

					<?php
						$total_price = $i = 0;
						foreach ( $Cart->getItems() as $order_code=>$quantity ) :
							$total_price += $quantity*$Cart->getItemPrice($order_code);
					?>
						<?php echo $i++%2==0 ? "<tr>" : "<tr class='odd'>"; ?>
							<td class="quantity center"><input type="text" name="quantity[<?php echo $order_code; ?>]" size="3" value="<?php echo $quantity; ?>" tabindex="<?php echo $i; ?>" /></td>

							<td class="item_name"><?php echo $Cart->getItemName($order_code); ?></td>
							<td class="order_code"><?php echo $order_code; ?></td>
							<td class="unit_price">$<?php echo $Cart->getItemPrice($order_code); ?></td>

							<td class="extended_price">$<?php echo ($Cart->getItemPrice($order_code)*$quantity); ?></td>
							<td class="remove center"><input type="checkbox" name="remove[]" value="<?php echo $order_code; ?>" /></td>
						</tr>

					<?php endforeach; ?>
					<tr><td colspan="4"></td><td id="total_price">$<?php echo $total_price; ?></td></tr>
				</table>
				<input type="submit" name="update" value="Update cart" />

			</form>

			<?php else: ?>
				<p class="center">You have no items in your cart.</p>
			<?php endif; ?>
			<p><a href="load.php">Load Sample Cart</a></p>

		</div>
	</body>
</html>

Here we just show the items in the cart in a nicely formatted table with some form controls for removing items and changing the quantities. On line 18 we check to see if there are items in the cart. If there are, we go ahead and create the table. If not, we show a simple message letting the user know she doesn't have any items in her cart. I'm using the alternative syntax for if...else statements.

This chunk of code might look intimidating but it's pretty simple if we break it down:

<?php
	$total_price = $i = 0;
	foreach ( $Cart->getItems() as $order_code=>$quantity ) :
		$total_price += $quantity*$Cart->getItemPrice($order_code);
?>

	<?php echo $i++%2==0 ? "<tr>" : "<tr class='odd'>"; ?>
		<td class="quantity center"><input type="text" name="quantity[<?php echo $order_code; ?>]" size="3" value="<?php echo $quantity; ?>" tabindex="<?php echo $i; ?>" /></td>

		<td class="item_name"><?php echo $Cart->getItemName($order_code); ?></td>
		<td class="order_code"><?php echo $order_code; ?></td>
		<td class="unit_price">$<?php echo $Cart->getItemPrice($order_code); ?></td>

		<td class="extended_price">$<?php echo ($Cart->getItemPrice($order_code)*$quantity); ?></td>
		<td class="remove center"><input type="checkbox" name="remove[]" value="<?php echo $order_code; ?>" /></td>
	</tr>

<?php endforeach; ?>

First we set the total price ($total_price) and a row counting variable ($i) to 0. Then we dive into a foreach loop that creates a row in the table for each item. Here's a run down of what happens inside the loop:

  1. Add the extended price (quantity * unit price) to the total price.
    $total_price += $quantity*$Cart->getItemPrice($order_code);
  2. Echo out the opening <tr> tag. If the row count is odd, include a class called "odd". This is for the zebra striping that will make browsing the cart easier. Here we use the ternary operator (?:) as a shortcut to a full if...else statement.
    echo $i++%2==0 ? "<tr>" : "<tr class='odd'>";
  3. Echo out the input quantity field. The name of the input field is formatted (quantity[ORDER-CODE]) so PHP will automatically convert it into an array. We reuse the row count ($i) to add a tab index.
    <td class="quantity center"><input type="text" name="quantity[<?php echo $order_code; ?>]" size="3" value="<?php echo $quantity; ?>" tabindex="<?php echo $i; ?>" /></td>
  4. Echo out the item name, order code, unit price, and extended price.
    <td class="item_name"><?php echo $Cart->getItemName($order_code); ?></td>
    <td class="order_code"><?php echo $order_code; ?></td>
    <td class="unit_price">$<?php echo $Cart->getItemPrice($order_code); ?></td>
    
    <td class="extended_price">$<?php echo ($Cart->getItemPrice($order_code)*$quantity); ?></td>
  5. Echo out the remove item checkbox. Notice again, the specially formatted name of the checkbox input element.
    <td class="remove center"><input type="checkbox" name="remove[]" value="<?php echo $order_code; ?>" /></td>
    
    

Afer the foreach loop we echo out another row that shows the total price of all the items in the cart. I've also added a link to the load.php we created in the previous step so we can load in sample data for testing easily.

Step 6 - Styling the cart

The cart looks a little plain at the moment so lets give it some styling. Save the CSS code as cart.css in cart/css/ folder. This will give the cart some color and formatting so that it's easier on the eyes.

body {
	color: #222;
	font: 0.8em Arial, Helvetica, sans-serif;
}

h1 {
	font: 2em normal Arial, Helvetica, sans-serif;
	margin-bottom: 0.5em;
}

#container {
	margin: 0 auto;
	width: 80%;
}

table#cart {
	border-collapse: collapse;
	margin-bottom: 1em;
	width: 100%;
}
	
	table#cart th {
		background: #006b68;
		color: #fff;
		text-align: left;
		white-space: nowrap;
	}
	
	table#cart th,
	table#cart td {
		padding: 5px 10px;
	}
	
	table#cart .item_name {
		width: 100%;
	}
	
	table#cart .quantity input {
		text-align: center;
	}
	
	table#cart tr td {
		background: #fff;
	}
	
	table#cart tr.odd td {
		background: #eee;
	}
	
	.center {
		text-align: center;
	}

Step 7 - Processing the cart

Now we need to write the script that handles adding, removing, and setting quantities for items. Save this one as cart_action.php in the cart/ folder.

<?php

include('shopping_cart.class.php');
session_start();
$Cart = new Shopping_Cart('shopping_cart');

if ( !empty($_GET['order_code']) && !empty($_GET['quantity']) ) {
	$quantity = $Cart->getItemQuantity($_GET['order_code'])+$_GET['quantity'];
	$Cart->setItemQuantity($_GET['order_code'], $quantity);
}

if ( !empty($_GET['quantity']) ) {
	foreach ( $_GET['quantity'] as $order_code=>$quantity ) {
		$Cart->setItemQuantity($order_code, $quantity);
	}
}

if ( !empty($_GET['remove']) ) {
	foreach ( $_GET['remove'] as $order_code ) {
		$Cart->setItemQuantity($order_code, 0);
	}
}

$Cart->save();

header('Location: cart.php');

?>

This is another fairly simple script. There are three if statements to check for adding items, setting quantities, and removing items. Here's where the special formatting on the input field names comes in play. PHP will automatically convert input field names with brackets into arrays. So if we do a var_dump() of $_GET on the update cart form submission, you might get something that looks like this:

array(3) {
  ["quantity"]=>
  array(3) {
    ["HSD-KSE"]=>
    string(1) "2"
    ["KLS-IEN"]=>
    string(1) "1"
    ["KWL-JFE"]=>
    string(1) "9"
  }
  ["remove"]=>
  array(2) {
    [0]=>
    string(7) "KLS-IEN"
    [1]=>
    string(7) "KWL-JFE"
  }
  ["update"]=>
  string(11) "Update cart"
}

Since all the new quantities and items to be removed are in arrays, we can simply loop over them using a foreach loop and call the appropriate functions. The first if statement adds new items to the cart, the second changes item quantities, and the third removes items.

At this point, we have a functioning shopping cart without the AJAX. Pretty boring so far, but we'll be adding in the AJAX in the next step.

Step 8 - Adding AJAX

The first thing we need to do is link jQuery, the color animation plugin, and our own javascript, which we will be creating in a little bit, to the cart. Open up cart.php again and add the following lines inside the <head></head> tags.

<script src="js/jquery-1.2.6.pack.js" type="text/javascript"></script>
<script src="js/jquery.color.js" type="text/javascript"></script>
<script src="js/cart.js" type="text/javascript"></script>

Now create a file called cart.js inside the cart/js/ folder. This is where we will be putting our own Javascript code that enables all the AJAX functionality. Inside it, add the following code.

$(function() {
	$("#cart tr .remove input").click(function() {
		var orderCode = $(this).val();
		$.ajax({
			type: "GET",
			url: "cart_action.php",
			data: "remove[]=" + orderCode,
			success: function() {
				$("#cart tr .remove input[value=" + orderCode + "]").parent().parent().fadeOut(500, function() {
					$(this).remove();
					calcPrice();
				});
			},
			error: function() {
				window.location("cart_action.php?remove[]="+orderCode);
			}
		});
	});
	
	$("#cart tr .quantity input").change(function() {
		var orderCode = $(this).attr("name").slice(9, -1);
		var quantity = $(this).val();
		$.ajax({
			type: "GET",
			url: "cart_action.php",
			data: "quantity[" + orderCode + "]=" + quantity,
			success: function() {
				var startColor = $("#cart tr .quantity input[name*=" + orderCode + "]").parent().parent().hasClass("odd") ? "#eee" : "#fff";
				$("#cart tr .quantity input[name*=" + orderCode + "]").parent().parent().find("td").animate({ backgroundColor: "#ff8" }, 100).animate({ backgroundColor: startColor }, 800);
				calcPrice();
			},
			error: function() {
				window.location("cart_action.php?quantity[" + orderCode + "]=" + quantity);
			}
		});
	});
});

function calcPrice() {
	var totalPrice = 0;
	$("#cart tr .quantity").parent().each(function() {
		var quantity = $(".quantity input", this).val();
		var unitPrice = $(".unit_price", this).text().slice(1);
		var extendedPrice = quantity*unitPrice;
		totalPrice += extendedPrice;
		
		$(".extended_price", this).html("$" + extendedPrice);
		$("#total_price").html("$"+totalPrice);
	});
	if ( totalPrice == 0 ) {
		$("#cart").parent().replaceWith("<p class='center'>You have no items in your cart.</p>");
	}
}

This jumble of code looks rather intimidating too, but it can be divided into three distinct blocks: the block that handles the remove checkboxes, the block that handles the quantity fields, and the last block that recalculates all the prices when a an item is removed or a quantity is changed. The first two blocks are contained in a function that looks like this:

$(function() {
	// Code goes here...
});

Code that goes inside this function is executed once the DOM has been loaded. It's a shortcut to the $(document).ready(callback) function.

The first block of code that goes inside that aforementioned function handles the remove checkboxes:

$("#cart tr .remove input").click(function() {
	var orderCode = $(this).val();
	$.ajax({
		type: "GET",
		url: "cart_action.php",
		data: "remove[]=" + orderCode,
		success: function() {
			$("#cart tr .remove input[value=" + orderCode + "]").parent().parent().fadeOut(500, function() {
				$(this).remove();
				calcPrice();
			});
		},
		error: function() {
			window.location("cart_action.php?remove[]="+orderCode);
		}
	});
});

This binds a function to the click event of all the checkboxes. When a checkbox is clicked, a couple things happen:

  1. Grab the order code and store it in a variable.
    var orderCode = $(this).val();
  2. Make an AJAX call to the server, telling it to remove the item. If you read Eric's tutorial on submitting forms without refreshing a page, this will look familiar. The data that is submitted is exactly the same as if we did a form submission. The data parameter is identical to the GET string we would see if we removed the redirect in cart_action.php and did an update cart form submission. If the AJAX call is successful, we fade out the row with the item we want to remove and then remove it completely from the DOM. Then we call the calcPrice() function (the third block of code) to recalculate all the prices. If the call was unsuccessful, we fallback to a page refresh.

The second block of code is very similar except it sets the quantities:

$("#cart tr .quantity input").change(function() {
	var orderCode = $(this).attr("name").slice(9, -1);
	var quantity = $(this).val();
	$.ajax({
		type: "GET",
		url: "cart_action.php",
		data: "quantity[" + orderCode + "]=" + quantity,
		success: function() {
			var startColor = $("#cart tr .quantity input[name*=" + orderCode + "]").parent().parent().hasClass("odd") ? "#eee" : "#fff";
			$("#cart tr .quantity input[name*=" + orderCode + "]").parent().parent().find("td").animate({ backgroundColor: "#ff8" }, 100).animate({ backgroundColor: startColor }, 800);
			calcPrice();
		},
		error: function() {
			window.location("cart_action.php?quantity[" + orderCode + "]=" + quantity);
		}
	});
});

Here we bind a function to the change event of all the quantity input fields that will execute an AJAX call whenever the quantities are changed. Let's break it down:

  1. Retrieve and store the order code and the new quantity.
    var orderCode = $(this).attr("name").slice(9, -1);
    var quantity = $(this).val();
  2. Make an AJAX call to the server telling it to update the specified quantity. If the call is successful, we make the background color of the row "blink" yellow for a second to let the user know the quantity has been changed, then call the calcPrice() function to recalculate all the prices. If the call is unsuccessful, fall back to a page refresh.

And finally the third block of code, which we've seen been called twice already: the calcPrice() function.

function calcPrice() {
	var totalPrice = 0;
	$("#cart tr .quantity").parent().each(function() {
		var quantity = $(".quantity input", this).val();
		var unitPrice = $(".unit_price", this).text().slice(1);
		var extendedPrice = quantity*unitPrice;
		totalPrice += extendedPrice;
		
		$(".extended_price", this).html("$" + extendedPrice);
		$("#total_price").html("$"+totalPrice);
	});
	if ( totalPrice == 0 ) {
		$("#cart").parent().replaceWith("<p class='center'>You have no items in your cart.</p>");
	}
}

This is simple as well. We loop through each row and recalculate the extended price and total price. Let's break it down what happens inside each loop:

  1. First retrieve the quantity and unit price of the item and multiply them to get the extended price. Then add it to the running total price which starts at zero.
    var quantity = $(".quantity input", this).val();
    var unitPrice = $(".unit_price", this).text().slice(1);
    var extendedPrice = quantity*unitPrice;
    totalPrice += extendedPrice;
  2. Update the extended price for the current row and the total price with the running total.
    $(".extended_price", this).html("$" + extendedPrice);
    $("#total_price").html("$"+totalPrice);
  3. If after we finish looping through the columns we find that all the items have been removed, replace the cart view with a message saying the cart is empty.
    if ( totalPrice == 0 ) {
    	$("#cart").parent().replaceWith("<p class='center'>You have no items in your cart.</p>");
    }

One thing to note is the selector I use to retrieve the rows in the table. I select all the table cells with the class of "quantity" and then call the parent() function to get the rows. This is because the table headers are also stored in a row. If we simply used "#cart tr", we would get the table headers as well.

Step 9 - "Add to cart"

No cart is complete without a way to add new items to the cart, so we're going to create an index page that shows off two different ways you can do just that. While we're at it, we're going to enable Thickbox so that the cart opens up in a modal window rather than going to a new page.

Let's create the page and then break it down. Save the following as index.html in the cart/ folder.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title>Shopping Cart</title>

		<script src="js/jquery-1.2.6.pack.js" type="text/javascript"></script>
		<script src="js/jquery.color.js" type="text/javascript"></script>
		<script src="js/thickbox.js" type="text/javascript"></script>
		<script src="js/cart.js" type="text/javascript"></script>
		<link href="css/style.css" rel="stylesheet" type="text/css" media="screen" />

		<link href="css/thickbox.css" rel="stylesheet" type="text/css" media="screen" />
		
		<script type="text/javascript">
			$(function() {
				$("form.cart_form").submit(function() {
					var title = "Your Shopping Cart";
					var orderCode = $("input[name=order_code]", this).val();
					var quantity = $("input[name=quantity]", this).val();
					var url = "cart_action.php?order_code=" + orderCode + "&quantity=" + quantity + "&TB_iframe=true&height=400&width=780";
					tb_show(title, url, false);
					
					return false;
				});
			});
		</script>
	</head>

	<body>
		<div id="container">
			<h1>Shopping Cart Demo</h1>
			<a href="cart.php?KeepThis=true&TB_iframe=true&height=400&width=780" title="Your Shopping Cart" class="thickbox">Open Cart</a>

			<hr />
			<a href="cart_action.php?order_code=KWL-JFE&quantity=3&TB_iframe=true&height=400&width=780" title="Your Shopping Cart" class="thickbox">Add three KWL-JFE to cart</a>
			<hr />
			<form class="cart_form" action="cart_action.php" method="get">

				<input type="hidden" name="order_code" value="KWL-JFE" />
				<label>KWL-JFE: <input class="center" type="text" name="quantity" value="1" size="3" ?></label>
				<input type="submit" name="submit" value="Add to cart" />
			</form>
		</div>

	</body>
</html>

If you take a look at the code between the <head></head> tags you'll notice I've included two more files, thickbox.js and thickbox.css, and added some more Javascript. Let's talk about the Thickbox bits first.

Thickbox converts links with a class of "thickbox" into a link that opens up in a modal window. The different options for the modal window are defined in the GET string of the URL. The different options are detailed in the examples section of the Thickbox site. For our shopping cart, we are interested in opening iFramed content in a modal window.

To open iFrame content we use the following parameters in the URL:

?KeepThis=true&TB_iframe=true&height=400&width=600

The first two parameters, KeepThis and TB_iframe, are constant but the other two define the height and width of the modal window. We'll make ours 780px wide and 400px tall. Our open cart link will look like this (don't forget to set the class to "thickbox" or it won't work!):

<a href="cart.php?KeepThis=true&TB_iframe=true&height=400&width=780" title="Your Shopping Cart" class="thickbox">Open Cart</a>

Another thing to note is that the title attribute of the link will be displayed as the title of the modal window.

The next link will add an item to the cart in addition to opening it. To do this we have to pass two more parameters in the GET query string: order_code and quantity. However, these two parameters need to come before the KeepThis parameter in the query--Thickbox automatically removes all parameters after the KeepThis parameter. The URL should look something like this:

cart_action.php?order_code=KWL-JFE&quantity=1&TB_iframe=true&height=400&width=780

This URL will add a single item with the order code of KWL-JFE. The cart_action.php script we wrote previously will look for the order code and quantity parameters and add them to the cart accordingly.

The second way we can add items to the cart is by form that lets the user specify the quantity. However, since we want the cart to open up in a Thickbox, we have to use a bit of Javascript. Between the <head></head> tags you'll notice we have some Javascript that executes once the DOM has been loaded:

<script type="text/javascript">
	$(function() {
		$("form.cart_form").submit(function() {
			var title = "Your Shopping Cart";
			var orderCode = $("input[name=order_code]", this).val();
			var quantity = $("input[name=quantity]", this).val();
			var url = "cart_action.php?order_code=" + orderCode + "&quantity=" + quantity + "&TB_iframe=true&height=400&width=780";
			tb_show(title, url, false);
			
			return false;
		});
	});

</script>

This code looks for forms with a class of "cart_form" and binds a function to the submit event. The handler can be broken down as follows:

  1. Set the title of the window and get the order code and quantity from the form fields.
  2. Build a URL with order_code, quantity, and the Thickbox parameters.
  3. Open a Thickbox modal window.
  4. Return false to stop the form submission.

Finally, we'll add a little bit of CSS to give it some style. Save the following code as style.css in the cart/css/ folder.

body {
	color: #222;
	font: 0.8em Arial, Helvetica, sans-serif;
}

h1 {
	font: 2em normal Arial, Helvetica, sans-serif;
	margin-bottom: 0.5em;
}

#container {
	margin: 0 auto;
	width: 80%;
}

table#cart {
	border-collapse: collapse;
	margin-bottom: 1em;
	width: 100%;
}
	
	table#cart th {
		background: #006b68;
		color: #fff;
		text-align: left;
		white-space: nowrap;
	}
	
	table#cart th,
	table#cart td {
		padding: 5px 10px;
	}
	
	table#cart .item_name {
		width: 100%;
	}
	
	table#cart .quantity input {
		text-align: center;
	}
	
	table#cart tr td {
		background: #fff;
	}
	
	table#cart tr.odd td {
		background: #eee;
	}
	
	.center {
		text-align: center;
	}

The final product

You're done! Well, you're done with this tutorial. There is still some coding that needs to be done to adapt this to the requirements of your site.

Next steps

As I've mentioned several times before, there are still some key parts of the cart we just created that are missing. These parts depend on the requirements of your site. For instance: most online shopping sites will have a database storing all the product information, but the structure of this database varies widely. Methods in the Shopping_Cart class that retrieve item names, prices, and other information will need database code.

Another important thing to add is input validation. Since much of the data is passed along via GET, it would not be hard for someone to start putting in random order codes and non-numeric quantities. These two things should definitely be validated before adding an item to the cart.


Related Posts

Check out some more great tutorials and articles that you might like

Enjoy this Post?

Your vote will help us grow this site and provide even more awesomeness

Plus Members

Source Files, Bonus Tutorials and
More for $9 a month for all TUTS+
sites in one subscription.

Join Now

User Comments

( ADD YOURS )
  1. PG

    David August 21st

    Oh - this is great - and it degrades nicely too … playing well without javascript. Congratulations - an excellent tutorial!

    ( Reply )
  2. PG

    Philo August 21st

    Awesome tutorial! Well explained! :)

    ( Reply )
  3. PG

    Maicon August 21st

    I was searching for some like this a long time. Thank you man.

    ( Reply )
  4. PG

    cheese August 21st

    looks cool, thanks!

    didn’t like the way it auto-removes things without me pressing update tho

    ( Reply )
  5. PG

    insic August 21st

    this is really great. i should apply this in my future projects.

    ( Reply )
    1. PG

      Aziz April 29th

      fine…

      ( Reply )
  6. PG

    Craigsnedeker August 21st

    Cool tut! Thanks!

    Can you guys think about adding a new tutorial site for computers and tech stuff?
    thanks

    ( Reply )
  7. PG

    Jaswinder Virdee August 21st

    Nice and simple shopping cart alot like fatfreecart, hope to see a follow up tutorial with more advanced features.

    ( Reply )
  8. PG

    Gareth Price August 21st

    Looks great! Thanks NetTuts :)

    ( Reply )
  9. PG

    Shane August 21st

    Wahey - great stuff! Nice tutorial - thanks for posting it. :)

    ( Reply )
  10. PG

    Taylor Satula August 21st

    Great tut Doesn’t work in ie7 . Who would have guessed it?

    ( Reply )
  11. PG

    Thomas Milburn August 21st

    Well done with this tutorial. It’s well written and goes into just enough detail. This is the type of tutorial I expect from Nettuts all the time. Just a quick typo in your code:

    if ( !emptyempty($_GET['order_code']) && !emptyempty($_GET['quantity']) ) {

    should be:

    if ( !empty($_GET['order_code']) && !empty($_GET['quantity']) ) {

    Otherwise a brilliant example of how to uses OOP and accessible Javascript. Another good cart tutorial is http://www.phpwebcommerce.com which covers integration with a database and paypal however it doesn’t cover OOP, CSS or Javascript.

    Thanks again James!

    ( Reply )
  12. PG

    Dan August 21st

    Nice, wish I had something to sell!

    ( Reply )
  13. PG

    Yusuf August 21st

    me too, wish i had something to sell too!

    ( Reply )
  14. PG

    ST August 21st

    In the quantity field enter and see what happens. I realize the scope of this is not meant to cover filtering and what not but something as simple as the setting the maxlength attribute would help.

    ( Reply )
  15. PG

    James August 21st

    Just had a quick through and I gotta say that it’s a very good tutorial! I’m going to give it a proper read when I have some time cos’ it’s quite detailed and I’m quite tired… Anyway nice tut James! … I’m sure I’ll find something to crit on when I’m more awake! ;-)

    ( Reply )
  16. PG

    ST August 21st

    Hmmm, ok. The comment field at nettuts is properly filtered! Good. after “enter” and before “and see what happens” just throw in a php echo statement of some sort.

    ( Reply )
  17. PG

    Ben Grifffiths August 21st

    Interesting and very well covered, thanks :)

    ( Reply )
  18. PG

    Jonathan Melo August 21st

    This website is such a great find. Can’t wait to try it, thanks!

    ( Reply )
  19. PG

    Lamin Barrow August 21st

    Cool tut. Thanks.

    ( Reply )
  20. PG

    Gio Borje August 21st

    I love this site. A tutorial right when I need it.

    ( Reply )
  21. PG

    Braden Keith August 21st

    Jeez, extensive. Nice, very nice

    ( Reply )
  22. PG

    Chris August 21st

    How about an AJAX checkout process to go along with it? A possible 2ndd i in the series?

    ( Reply )
  23. PG

    Jay Salvat August 22nd

    Nice one.

    There are some typos in the Step 7.

    if ( !emptyempty($_GET['order_code']) && !emptyempty($_GET['quantity']) ) {
    if ( !emptyempty($_GET['quantity']) ) {
    if ( !emptyempty($_GET['remove']) ) {

    > !empty instead of !emptyempty

    ( Reply )
  24. PG

    Moksha August 22nd

    How can we do it in asp.net

    ( Reply )
  25. PG

    Raj August 23rd

    very nice tutorial :) Thanx a bunch!

    ( Reply )
  26. PG

    David August 23rd

    Shouldn’t you be using a POST ajax request rather than GET for removing cart items etc. since its modifying data on the server rather than just requesting data. Ideally it’d be a DELETE request for removing items if that were possible in current browsers.

    ( Reply )
  27. PG

    vorp August 30th

    If i take 1pc. HSD-KSE and 6pc. of ELS-OWK and correct it to like 5 the total amount will be $59.940000000000005 same with other numbers - so what went wrong there?.)

    ( Reply )
  28. PG

    Jon August 31st

    Very well explained - I am going to try and get this working soon for my site and maybe make a $ or two!

    ( Reply )
  29. PG

    Gav September 1st

    Hi guys,

    I get the following errors on my webserver when I use the source code from this page and try to add an item to the cart - Has anyone else come across this issue, if so did he/she manage to find a fix? Thanks in advance

    Invalid argument supplied for foreach() in /usr/home/gavin/public_html/_testing/cart/cart_action.php on line 13

    Cannot modify header information - headers already sent by (output started at /usr/home/gavin/public_html/_testing/cart/cart_action.php:13) in /usr/home/gavin/public_html/_testing/cart/cart_action.php on line 26

    ( Reply )
  30. PG

    Gav September 1st

    Hi,

    It seems the errors I posted in my post directly above this way only appear on my webserver when using Firefox (version 3) - it doesnt do the same thing when using Internet Explorer (v7)

    Any ideas??

    Cheers

    ( Reply )
  31. PG

    devmug September 2nd

    I also get the
    error Cannot modify header information - headers already sent by (output started at /usr/home/gavin/public_html/_testing/cart/cart_action.php:13)
    when adding a new item in both Firefox and IE7.

    To solve the problem .. I used a elseif rather than a plain if statement in the action cart line 12.
    The problem seems to be that when adding a item we only need the first of three if statements to execute rather than the first and the second.

    hope this is clear. if not simply replace ..
    if ( !empty($_GET['quantity']) ) {

    with

    elseif ( !empty($_GET['quantity']) ) {

    Cheers ..

    ( Reply )
  32. PG

    satrianivzla September 2nd

    Gav you are not the only 1 who has problems with this sample code, to fix some of the errors that you listed, you could make a little changes on the to of some files , for example:

    Step 4: load.php
    where you can find these lines
    include(’shopping_cart.class.php’);
    session_start();

    Should be
    session_start();
    include(’shopping_cart.class.php’);

    Step 7: cart.php
    Where you find these lines
    include(’shopping_cart.class.php’);
    session_start();
    $Cart = new Shopping_Cart(’shopping_cart’);

    Should be
    ob_start();
    session_start();
    include(’shopping_cart.class.php’);

    and also try adding these lines at the end of the file

    Also, on the index.html you will need to fix line 35
    KWL-JFE:
    Should be
    KWL-JFE:

    And yes you are right I tested the sample code on firefox and IE7 and didn’t worked as good as the sample that is running on this server. In my case load.php doesn’t work and I can’t see the shopping cart item once I add any product.

    Good luck

    ( Reply )
  33. PG

    Gav September 7th

    Hi Guys,

    Thanks again for the help - I have made some amendments to get this working - if anyone wants this i’ll happily give them the alterations i’ve made to get it running smoothly - thanks again this is a great tutorial!

    ( Reply )
  34. PG

    Matty September 18th

    Hi Gav,
    I’ve fixed the main things alone, but load.php doesn’t load.
    Could you show me the source of your fixed version?

    I can’t run it on localhost (my echoed parts appear as code)
    It runs better on my server.

    Anyway, great tut.

    Thx in advance

    ( Reply )
  35. PG

    wuwongy September 22nd

    Hi All,

    Great little php cart, this will be really useful. I have the example up and running.
    Has anyone got any ideas about the best practice for passing actual prices and product names (aswell as codes) through to the cart as the example doesn’t do this yet.
    Cheers

    ( Reply )
  36. PG

    Matty September 22nd

    OK, I’ve got it working.

    How?

    I upgraded my server to PHP5, and it worked like a charm.

    I couldn’t solve the problem, but I know it came from the $_SESSION vars.
    I guess the method built doesn’t like PHP 4..

    ( Reply )
  37. PG

    Marty September 23rd

    well done,
    very nice read & understanding..

    I’ll have to give this one a go im sure..

    ( Reply )
  38. PG

    John September 25th

    This is great, but how do you add a checkout?
    If someone could please tell, my email address is: johnlone78@yahoo.com.au

    ( Reply )
  39. PG

    Phil H September 25th

    vorp: You can fix the amount issue by changing the following php code from line 50 in cart.php from:

    to

    ( Reply )
  40. PG

    Gav September 25th

    To Matty,

    Sorry I’ve been away for abit - if you or anyone else has problems just let me know - got it working well on my php5 locat installation - if any wants the source just let me know :)

    ( Reply )
  41. PG

    Gav September 27th

    To previous post by ‘John’ - I wouldn’t call your manner ‘polite’ - and most of the people on here I find generally helpful.

    Furthermore, you said the following “You need to know how to code in order to get it working” - no offence but if you cannot code as suggest why are you trying to implement a shopping cart??

    Feel free to ask ahead for advice and solutions but don’t expect people to give you sympathy when you rant like that - thats just my view.

    ( Reply )
  42. PG

    np25 October 4th

    This is completely useless, it is just a pretty interface. What was the point of creating this article?

    ( Reply )
  43. PG

    Gavin October 11th

    Hi guys,

    Can anyone work out how to get the shopping cart to calculate values to 2 decimal places - in cart.js, there is the function calcPrice() - this works however prices show as illustrated below:

    $164.67000000000002

    rather than

    164.67

    I know thr php function number_format() would rectify this but this is all done within the javascript file - anyone got any solutions for this :)

    ( Reply )
  44. PG

    Angelina October 17th

    Online shopping is now a easy way of buying anything which they want by using the internet.so people want to know more about this.
    Its a nice post about the same thing.
    Thanks

    ( Reply )
  45. PG

    Juan October 21st

    Great! very usefull. Thanks

    ( Reply )
  46. PG

    DanC October 22nd

    Hi there,

    I love the tut but am really struggling to find the right code for giving individual items different prices.

    Can anyone help? I know it has to be easy but I just can’t figure it out!

    Cheers,

    Dan

    ( Reply )
  47. PG

    DanC October 23rd

    And if anyone has tips for how to integrate with google checkout and paypal, that would also be appreciated!

    Cheers,

    ( Reply )
  48. PG

    Danc October 24th

    Ok have figured out the checkouts!

    Now just the prices! I have been messing around and added stuff to pretty much all the pages but cannot get anything to work!

    If anyone is out there with a solution, please let me know!

    Nice one guys,

    DanC

    ( Reply )
  49. PG

    Gav October 30th

    Hi Dan,

    This could be easily done using a database, in the example each has a unique ‘Order Code’

    Would just be a case of getting the related order code for that product, for the purpose the example you could just for testing make a simple IF statement such as..
    if ($code == ‘XYY’)
    $price = 19.99

    etc.. let me know if you need further clarification :)

    ( Reply )
  50. PG

    himmih November 1st

    thx a good tutorial

    ( Reply )
  51. PG

    Domi November 8th

    Nice work, but where’s the code again to adapt those decimal numbers in the prices ?

    Very good tutorial otherwise, works in IE and FF, but not (yet) in Opera or Safari.

    ( Reply )
  52. PG

    OldManRiver November 13th

    How would you modify the code to have category headers above your existing datarows that are collapsible?

    Futher, for repeat orders, I have to redisplay last order with Qtys. How would you process to have all categories collapsed (no data showing) for categories where nothing was previously ordered?

    ( Reply )
  53. PG

    OldManRiver November 14th

    All,

    Have code working as fix for collapsible category headers at:

    http: // forums.devshed.com/ php-development-5/ collapsing-rows-570247. html

    Now working to get last order, then show only categories open with item in order.

    Cheers!

    OMR

    ( Reply )
  54. PG

    Devin Rajaram November 20th

    Hey how can I add a checkout button where it adds up all the stuff in the cart and a customer can pay with paypal or creditcards etc

    ( Reply )
  55. PG

    MMF November 23rd

    Nice !

    ( Reply )
  56. PG

    kareem November 24th

    this is wonderful tutorial i will put acopy of this lesson on
    my site here
    http://www.as7ap4you.com

    ( Reply )
  57. PG

    Danc November 25th

    I was wondering if anyone has any tips on integrating some of the javascript (ie the remove and quantity update parts) in to another basket design?

    I am struggling again!

    Cheers in advance!

    ( Reply )
  58. PG

    Kevin.Y December 9th

    Is fantastic.

    ( Reply )
  59. PG

    Izul Cyber Cafe December 24th

    Oww… Very Nice..

    thanks…

    ( Reply )
  60. PG

    nissanguney December 28th

    Thanks

    ( Reply )
  61. PG

    OldManRiver December 30th

    I have an existing shopping cart, that I had to move into an IFRAME for the customers. I tried using the code here, but the array never updates.

    I think it has to do with how vars and session vars pass to/from IFRAMEs.

    Anyone know how to solve this

    ( Reply )
  62. PG

    Alexandre MONTI(FI'm french^^) January 27th

    Great taff.
    Thanks a lot

    ( Reply )
  63. How can we do it in xml ?

    ( Reply )
  64. How can we do it in xml ?
    - I am have xml documents, your product setuping.

    ( Reply )
  65. PG

    baranitharan February 14th

    this one is very nice…and very very useful to me

    ( Reply )
  66. PG

    Venky March 13th

    Can u give me example in asp.net code

    With Thanks
    Venky

    ( Reply )
  67. PG

    Ronald March 17th

    Fix the error in cart_action.php by changing the second if condition to

    if ( is_array($_GET['quantity']) && count($_GET['quantity']))

    That’ll get rid of the error fix the update functionality.

    Don’t remove the 2nd and 3rd if blocks as suggested somewhere above. That will make the error go away but will disable the removal and update actions.

    ( Reply )
  68. PG

    Rohan Raj April 1st

    HI

    I m a web designer. i want to learn AJAX but don’t have any knowledge about AJAX. I have knowledge about HTML & CSS.

    So, Plz guide me How to start my study(learning) for AJAX.

    ( Reply )
  69. PG

    peer April 2nd

    great tut!
    just a question:

    - how could i make this work on php4?

    thanks
    p-.

    ( Reply )
  70. PG

    john April 9th

    Nice blogs!! good jobs

    ( Reply )
  71. PG

    Majstor April 9th

    I can’t make it work on IE6.
    When I click add to chart, I get message:
    PHP Notice: Undefined index: shopping_cart in shopping_cart.class.php on line 16

    Can anyone help?

    ( Reply )
  72. PG

    Majstor April 9th

    I can’t make it work on IE6.
    When I click button “add to cart” i get error:
    PHP Notice: Undefined index: shopping_cart in shopping_cart.class.php on line 16

    Can anyone help?

    ( Reply )
  73. PG

    med April 11th

    plz how to change price to $variable
    it’s always 9.99
    thanx

    ( Reply )
  74. PG

    sam April 21st

    Nice Work Done Im making some think like that but my goal is not to reload the page even a single time after the first load and all the things should be handled through ajax that is trough XMLhttp or Activexttp

    so the main problem i face with the intregration of jquery and my own written ajax can u help me about that i call php files that then return some html that is then displayed in div’s

    ( Reply )
  75. PG

    CgBaran Tuts May 5th

    Great tutorial thanks

    ( Reply )
  76. PG

    Rita Fonseca May 14th

    Great tt, but i have a problem with the library thickBock, When i want to show the cart it supossed to put the background in black(i does that), an show the Page which is cart.php but…… T_T It only shows the half of it the cannot see it please hellp already try to fix changhing the js but it does not work please help, and yes i add some code to the js but when put again the demo version it didnt change back… T_T

    please help..!!!!! =(

    ( Reply )
  77. PG

    Carlos Medeiros July 2nd

    I do have a simple question. Which is your choice of web development application? I am trying dreamweaver but it seems unnecessirily expensive.

    ( Reply )
  1. Arrow
    Gravatar

    Your Name
    July 2nd