only for RuBoard - do not distribute or recompile Previous Section Next Section

Implementing the Shopping Cart

The shopping cart functionality all revolves around a session variable called $cart. This is an associative array that has ISBNs as keys and quantities as values. For example, if I add a single copy of this book to my shopping cart, the array would contain

					
0672317842 => 1

				

That is, one copy of the book with the ISBN 0672317842. When we add items to the cart, they will be added to the array. When we view the cart, we will use the $cart array to look up the full details of the items in the database.

We also use two other session variables to control the display in the header that shows Total Items and Total Price. These variables are called $items and $total_price, respectively.

Using the show_cart.php Script

Let's begin looking at how the shopping cart code is implemented by looking at the show_cart.php script. This is the script that displays the page we will visit if we click on any View Cart or Add to Cart links. If we call show_cart.php without any parameters, we will get to see the contents of it. If we call it with an ISBN as parameter, the item with that ISBN will be added to the cart.

To understand this fully, look first at Figure 25.6.

Figure 25.6. The show_cart.php script with no parameters just shows us the contents of our cart.
graphics/25fig06.gif

In this case, we have clicked the View Cart link when our cart is empty; that is, we have not yet selected any items to purchase.

Figure 25.7 shows our cart a bit further down the track when we have selected two books to buy. In this case, we have gotten to this page by clicking the Add to Cart link on the show_book.php page for this book, PHP and MySQL Web Development. If you look closely at the URL bar, you will see that we have called the script with a parameter this time. The parameter is called new and has the value 0672317842—that is, the ISBN for the book we have just added to the cart.

Figure 25.7. The show_cart.php script with the new parameter adds a new item to the cart.
graphics/25fig07.gif

From this page, you can see that we have two other options. There is a Save Changes button that we can use to change the quantity of items in the cart. To do this, you can alter the quantities directly and click Save Changes. This is actually a submit button that takes us back to the show_cart.php script again to update the cart.

In addition, there's a Go To Checkout button that a user can click when she is ready to leave. We'll come back to that in a minute.

For now, let's look at the code for the show_cart.php script. This code is shown in Listing 25.9.

Listing 25.9 show_cart.php—This Script Controls the Shopping Cart
<?
  include ('book_sc_fns.php');
  // The shopping cart needs sessions, so start one
  session_start();

  if($new)
  {
    //new item selected
    if(!session_is_registered("cart"))
    {
      $cart = array();
      session_register("cart");
      $items = 0;
      session_register("items");
      $total_price = "0.00";
      session_register("total_price");
    }
    if($cart[$new])
      $cart[$new]++;
    else
      $cart[$new] = 1;
    $total_price = calculate_price($cart);
    $items = calculate_items($cart);
  
  }
  if($save)
  {
    foreach ($cart as $isbn => $qty)
    {
      if($$isbn=="0")
        unset($cart[$isbn]);
      else
        $cart[$isbn] = $$isbn;
    }
    $total_price = calculate_price($cart);
    $items = calculate_items($cart);
  }

  do_html_header("Your shopping cart");

  if($cart&&array_count_values($cart))
    display_cart($cart);
  else
  {
    echo "<p>There are no items in your cart";
    echo "<hr>";
  }
  $target = "index.php";

  // if we have just added an item to the cart,
  // continue shopping in that category
  if($new)
  {
    $details =  get_book_details($new);
    if($details["catid"])
      $target = "show_cat.php?catid=".$details["catid"];
  }
  display_button($target, "continue-shopping", "Continue Shopping");
  $path = $PHP_SELF;
  $path = str_replace("show_cart.php", "", $path);
  display_button("https://".$SERVER_NAME.$path."checkout.php",
                 "go-to-checkout", "Go To Checkout");
  do_html_footer();
?>

There are three main parts to this script: displaying the cart, adding items to the cart and saving changes to the cart. We'll cover these in the next three sections.

Viewing the Cart

No matter which page we have come from, we will display the contents of the cart. In the base case, when a user has just clicked View Cart, this is the only part of the code that will be executed, as follows:

						
if($cart&&array_count_values($cart))
  display_cart($cart);
else
{
  echo "<p>There are no items in your cart";
  echo "<hr>";
}

					

As you can see from this code, if we have a cart with some contents, we will call the display_cart() function. If the cart is empty, we'll give the user a message to that effect.

The display_cart() function just prints the contents of the cart as a readable HTML format, as you can see in Figures 25.6 and 25.7. The code for this function can be found in output_fns.php, which is included here as Listing 25.10. Although it is a display function, it is reasonably complex, so we include it here.

Listing 25.10 display_cart() Function from output_fns.php—This Function Formats and Prints the Contents of the Shopping Cart
function display_cart($cart, $change = true, $images = 1)
{
  // display items in shopping cart
  // optionally allow changes (true or false)
  // optionally include images (1 - yes, 0 - no)

  global $items;
  global $total_price;

  echo "<table border = 0 width = 100% cellspacing = 0>
        <form action = show_cart.php method = post>
        <tr><th colspan = ". (1+$images) ." bgcolor=\"#cccccc\">Item</th>
        <th bgcolor=\"#cccccc\">Price</th><th bgcolor=\"#cccccc\">Quantity</th>
        <th bgcolor=\"#cccccc\">Total</th></tr>";

  // display each item as a table row
  foreach ($cart as $isbn => $qty)
  {
    $book = get_book_details($isbn);
    echo "<tr>";
    if($images ==true)
    {
      echo "<td align = left>";
      if (file_exists("images/$isbn.jpg"))
      {
         $size = GetImageSize("images/".$isbn.".jpg");
         if($size[0]>0 && $size[1]>0)
         {
           echo "<img src=\"images/".$isbn.".jpg\" border=0 ";
           echo "width = ". $size[0]/3 ." height = " .$size[1]/3 .">";
         }
      }
      else
         echo "&nbsp;";
      echo "</td>";
    }
    echo "<td align = left>";
    echo "<a href = \"show_book.php?isbn=".$isbn."\">"
                    .$book["title"]."</a> by ".$book["author"];
    echo "</td><td align = center>$".number_format($book["price"], 2);
    echo "</td><td align = center>";

    // if we allow changes, quantities are in text boxes
    if ($change == true)
      echo "<input type = text name = \"$isbn\" value = $qty size = 3>";
    else
      echo $qty;
    echo "</td><td align = center>$".number_format($book["price"]*$qty,2)
         ."</td></tr>\n";
  }

  // display total row
  echo "<tr>
          <th colspan = ". (2+$images) ." bgcolor=\"#cccccc\">&nbsp;</td>
          <th align = center bgcolor=\"#cccccc\">
              $items
          </th>
          <th align = center bgcolor=\"#cccccc\">
              \$".number_format($total_price, 2).
          "</th>
        </tr>";

  // display save change button
  if($change == true)
  {
    echo "<tr>
            <td colspan = ". (2+$images) .">&nbsp;</td>
            <td align = center>
              <input type = hidden name = save value = true>
              <input type = image src = \"images/save-changes.gif\"
                     border = 0 alt = \"Save Changes\">
            </td>
            <td>&nbsp;</td>
        </tr>";
  }
  echo "</form></table>";
}

The basic flow of this function is as follows:

  1. Loop through each item in the cart, and pass the ISBN of each item to get_book_details() so that we can summarize the details of each book.

  2. Provide an image for each book, if one exists. We use the HTML image height and width tags to resize the image a little smaller here. This means that the images will be a little distorted, but they are small enough that this isn't much of a problem. (If it bothers you, you can always resize the images using the gd library discussed in Chapter 19, "Generating Images," or manually generate different size images for each product)

  3. Make each cart entry a link to the appropriate book, that is, to show_book.php with the ISBN as a parameter.

  4. If we are calling the function with the $change parameter set to true (or not set—it defaults to true), show the boxes with the quantities in them as a form with the Save Changes button at the end. (When we reuse this function after checking out, we won't want the user to be able to change her order.)

Nothing is terribly complicated in this function, but it does quite a lot of work, so you might find it useful to read it through carefully.

Adding Items to the Cart

If a user has come to the show_cart.php page by clicking an Add To Cart button, we have to do some work before we can show her the contents of her cart. Specifically, we need to add the appropriate item to the cart, as follows.

First, if the user has not put any items in her cart before, she will not have a cart, so we need to create one:

						
if(!session_is_registered("cart"))
{
  $cart = array();
  session_register("cart");
  $items = 0;
  session_register("items");
  $total_price = "0.00";
  session_register("total_price");
}

					

To begin with, the cart is empty.

Second, after we know that a cart is set up, we can add the item to it:

						
if($cart[$new])
  $cart[$new]++;
else
  $cart[$new] = 1;

					

Here, we are checking whether the item's already in the cart. If it is, we increment the quantity of that item in the cart by one. If not, we add the new item to the cart.

Third, we need to work out the total price and number of items in the cart. For this, we use the calculate_price() and calculate_items() functions, as follows:

						
$total_price = calculate_price($cart);
$items = calculate_items($cart);

					

These functions are located in the book_fns.php function library. The code for them is shown in Listings 25.11 and 25.12, respectively.

Listing 25.11 calculate_price() Function from book_fns.php—This Function Calculates and Returns the Total Price of the Contents of the Shopping Cart
function calculate_price($cart)
{
  // sum total price for all items in shopping cart
  $price = 0.0;
  if(is_array($cart))
  {
    $conn = db_connect();
    foreach($cart as $isbn => $qty)
    {
      $query = "select price from books where isbn='$isbn'";
      $result = mysql_query($query);
      if ($result)
      {
        $item_price = mysql_result($result, 0, "price");
        $price +=$item_price*$qty;
      }
    }
  }
  return $price;
}

As you can see, the calculate_price() function works by looking up the price of each item in the cart in the database. This is somewhat slow, so to avoid doing this more often than we need to, we'll store the price (and the total number of items, as well) as session variables and only recalculate when the cart changes.

Listing 25.12 calculate_items() Function from book_fns.php—This Function Calculates and Returns the Total Number of Items in the Shopping Cart
function calculate_items($cart)
{
  // sum total items in shopping cart
  $items = 0;
  if(is_array($cart))
  {
    foreach($cart as $isbn => $qty)
    {
      $items += $qty;
    }
  }
  return $items;
}

The calculate_items() function is simpler—it just goes through the cart and adds up the quantities of each item to get the total number of items.

Saving the Updated Cart

If we have come to the show_cart.php script from clicking the Save Changes button, the process is a little different. In this case, we have come via a form submission. If you look closely at the code, you will see that the Save Changes button is the submit button for a form. This form contains the hidden variable save. If this variable is set, we know that we have come to this script from the Save Changes button. This means that the user has presumably edited the quantity values in the cart, and we need to update them.

If you look back at the text boxes in the Save Changes form part of the script, you will see that they are named after the ISBN of the item that they represent, as follows:

						
echo "<input type = text name = \"$isbn\" value = $qty size = 3>";

					

Now look at the part of the script that saves the changes:

						
if($save)
  {
    foreach ($cart as $isbn => $qty)
    {
      if($$isbn=="0")
        unset($cart[$isbn]);
      else
        $cart[$isbn] = $$isbn;
    }
    $total_price = calculate_price($cart);
    $items = calculate_items($cart);
}

					

You can see that we are working our way through the shopping cart, and for each $isbn in the cart, we are checking the variable with that name, using the variable variable notation $$isbn.

(We covered variable variables in Chapter 1, "PHP Crash Course." ) As a reminder, when we refer to $$isbn, we are actually referring to the variable whose name is stored in $isbn. These are the form fields from the Save Changes form.

If any of the fields were set to zero, we remove that item from the shopping cart altogether, using unset(). Otherwise, we update the cart to match the form fields, as follows:

						
if($$isbn=="0")
  unset($cart[$isbn]);
else
  $cart[$isbn] = $$isbn

					

After these updates, we again use calculate_price() and calculate_items() to work out the new values of the $total_price and $items session variables.

Printing a Header Bar Summary

You will have noticed that in the header bar of each of the pages in the site, a summary of what's in the shopping cart is presented. This is obtained by printing out the values of the session variables $total_price and $items. This is done in the do_html_header() function.

These variables are registered when the user first visits the show_cart.php page. We also need some logic to deal with the cases where a user has not yet visited that page. This logic is also in the do_html_header() function:

						
if(!$items) $items = "0";
if(!$total_price) $total_price = "0.00";

					

Checking Out

When the user clicks the Go to Checkout button from the shopping cart, this will activate the checkout.php script. The checkout page and the pages behind it should be accessed via SSL, but the sample application does not force you to do this. (To read more about SSL, review Chapter 15, "Implementing Secure Transactions with PHP and MySQL." )

The checkout page is shown in Figure 25.8.

Figure 25.8. The checkout.php script gets the customer's details.
graphics/25fig08.gif

This script requires the customer to enter her address (and shipping address if it is different). It is quite a simple script, which you can see by looking at the code in Listing 25.13.

Listing 25.13 checkout.php—This Script Gets the Customer Details
<?
  //include our function set
  include ('book_sc_fns.php');

  // The shopping cart needs sessions, so start one
  session_start();

  do_html_header("Checkout");

  if($cart&&array_count_values($cart))
  {
    display_cart($cart, false, 0);
    display_checkout_form($HTTP_POST_VARS);
  }
  else
    echo "<p>There are no items in your cart";
  display_button("show_cart.php", "continue-shopping", "Continue Shopping");
  
  do_html_footer();
?>

There are no great surprises in this script. If the cart is empty, the script will notify the customer; otherwise, it will display the form shown in Figure 25.8.

If a user continues by clicking the Purchase button at the bottom for the form, she will be taken to the purchase.php script. You can see the output of this script in Figure 25.9.

Figure 25.9. The purchase.php script calculates shipping and the final order total, and gets the customer's payment details.
graphics/25fig09.gif

The code for this script is slightly more complicated than the code for checkout.php. It is shown in Listing 25.14.

Listing 25.14 purchase.php—This Script Stores the Order Details in the Database and Gets the Payment Details
<?
  include ('book_sc_fns.php');
  // The shopping cart needs sessions, so start one
  session_start();

  do_html_header("Checkout");

  // if filled out
  if($from=='process'||$cart&&$name&&$address&&$city&&$zip&&$country)
  {
    // able to insert into database
    if($from!='process')
    {
      if(!insert_order($HTTP_POST_VARS))
      {
        echo "Could not store data, please try again.";
        display_button("checkout.php", "back", "Back");
        do_html_footer();
        exit;
      }
    }
    //display cart, not allowing changes and without pictures
    display_cart($cart, false, 0);

    display_shipping(calculate_shipping_cost());

    //get credit card details
    display_card_form($HTTP_POST_VARS);

    display_button("show_cart.php", "continue-shopping", "Continue Shopping");
  }
  else
  {
    echo "You did not fill in all the fields, please try again.<hr>";
    echo "<form action = 'checkout.php'method = post>";

    // pass the data this page received back so the user won't have to re-enter
    foreach($HTTP_POST_VARS as $name => $value)
      echo "<input type = hidden name = $name value = '$value'>\n";
  
    display_form_button("back", "Back");
    echo "</form>";
  }

  do_html_footer();
?>

The logic here is straightforward: We check that the user filled out the form and inserted details into the database using a function called insert_order(). This is a simple function that pops the customer details into the database. The code for it is shown in Listing 25.15.

Listing 25.15 insert_order() Function from order_fns.php—This Function Inserts All the Details of the Customer's Order into the Database
function insert_order($order_details)
{
  global $total_price;
  global $cart;
  //extract order_details out as variables
  extract($order_details);


  //set shipping address same as address

  if(!$ship_name&&!$ship_address&&!$ship_city&&
!$ship_state&&!$ship_  zip&&!$ship_
country)
  {
    $ship_name = $name;
    $ship_address = $address;
    $ship_city = $city;
    $ship_state = $state;
    $ship_zip = $zip;
    $ship_country = $country;
  }

  $conn = db_connect();

  //insert customer address
  $query = "select customerid from customers where
            name = '$name'and address = '$address'
            and city = '$city'and state = '$state'
            and zip = '$zip'and country = '$country'";
  $result = mysql_query($query);
  if(mysql_numrows($result)>0)
  {
    $customer_id = mysql_result($result, 0, "customerid");
  }
  else
  {
    $query = "insert into customers values
            ('', '$name','$address','$city','$state','$zip','$country')";
    $result = mysql_query($query);
    if (!$result)
       return false;
  }
  $query = "select customerid from customers where
            name = '$name'and address = '$address'
            and city = '$city'and state = '$state'
            and zip = '$zip'and country = '$country'";
  $result = mysql_query($query);
  if(mysql_numrows($result)>0)
    $customerid = mysql_result($result, 0, "customerid");
  else
    return false;
  $date = date("Y-m-d");
  $query = "insert into orders values
            ('', $customerid, $total_price, '$date', 'PARTIAL', '$ship_name',
             '$ship_address','$ship_city','$ship_state','$ship_zip',
              '$ship_country')";
  $result = mysql_query($query);
  if (!$result)
    return false;

  $query = "select orderid from orders where
               customerid = $customerid and
               amount > $total_price-.001 and
               amount < $total_price+.001 and
               date = '$date' and
               order_status = 'PARTIAL' and
               ship_name = '$ship_name' and
               ship_address = '$ship_address' and
               ship_city = '$ship_city' and
               ship_state = '$ship_state' and
               ship_zip = '$ship_zip' and
               ship_country = '$ship_country'";
  $result = mysql_query($query);
  if(mysql_numrows($result)>0)
    $orderid = mysql_result($result, 0, "orderid");
  else
    return false;


  // insert each book
  foreach($cart as $isbn => $quantity)
  {
    $detail = get_book_details($isbn);
    $query = "delete from order_items where
              orderid = $orderid and isbn =  '$isbn'";
    $result = mysql_query($query);
    $query = "insert into order_items values
              ('$orderid', '$isbn', ".$detail["price"].", $quantity)";
    $result = mysql_query($query);
    if(!$result)
      return false;
  }

  return $orderid;
}
?>

This function is rather long because we need to insert the customer's details, the order details, and the details of each book they want to buy.

We then work out the shipping costs to the customer's address and tell them how much it will be with the following line of code:

						
display_shipping(calculate_shipping_cost());

					

The code we are using here for calculate_shipping_cost() always returns $20. When you actually set up a shopping site, you will have to choose a delivery method, find out how much it costs for different destinations, and calculate costs accordingly.

We then display a form for the user to fill in her credit card details using the display_card_form() function from the output_fns.php library.

only for RuBoard - do not distribute or recompile Previous Section Next Section