The symfony Cookbook

How to manage a Shopping Cart

Be trained by symfony experts
Dec 10: Paris (1.1 - Francais)
Dec 10: Atlanta (1.1 - English)
Dec 17: Montreal (1.1 - Francais)
Jan 21: Paris (1.1 - Francais)
Feb 18: Paris (1.1 - Francais)
and more...

About

You are currently reading "The symfony Cookbook" which is licensed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License license.

Search


powered by google

Chapter Content

Overview

Installation

Constructor

Create a User Shopping Cart

Add, modify and remove Items

The shoppingcart module

Add an item

Modify an item

Delete an entire shopping cart

Display the Shopping Cart in a Template

Get the content of the shopping cart

Pass the values to the template

With or without Taxes

You are currently browsing "The symfony Cookbook" in English for the 1.2 version. Switch to another version:
Creative Commons License This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License.
Translation of this work into another language is explicitly allowed.

Overview

Symfony offers a plugin to manage shopping carts in ebusiness websites. Adding an item, changing quantities and displaying the content of a shopping cart is made easy and painless.

Installation

The shopping cart classes are are not shipped with the default symfony installation, but packaged into a plugin called sfShoppingCart. Symfony plugins are installed via PEAR (find more about plugins in Chapter 17).

The installation of the sfShoppingCart plugin is very straightforward, as described in the plugin page. You just need to type in the command line:

$ symfony plugin:install sfShoppingCartPlugin

Then clear the cache to enable autoloading on the plugin's classes:

$ symfony cc

Constructor

The sfShoppingCart class is aimed to manage shopping carts. It can contain any kind of object.

The constructor allows to declare the tax rate to apply to the shopping cart:

$my_shopping_cart = new sfShoppingCart(sfConfig::get('app_cart_tax'));
 

In this example, the shopping cart tax rate is written in the app.yml configuration file of the application for easy change:

all:
  cart:
    tax:  19.6

Create a User Shopping Cart

You can easily create a new sfShoppingCart object in an action with the new constructor. However, it will not be of any use if it is not linked to a user session. The easiest way to keep a user's choice in a shopping cart is to make a composition of an sfShoppingCart object into the sfUser class. To do that, add a custom method to the myproject/apps/myapp/lib/myUser.php class:

class myUser extends sfUser
{
  public function getShoppingCart()
  {
    if (!$this->hasAttribute('shopping_cart'))
    {
      $this->setAttribute('shopping_cart', new sfShoppingCart(sfConfig::get('app_cart_tax')));
    }
 
    return $this->getAttribute('shopping_cart');
  }
 
  // ...
}
 

The $user->getShoppingCart() method will create a new shopping cart if the user doesn't already have one.

if you need more information about the way to override the default sfUser class by a custom myUser class, you should read the section about factories in Chapter 17.

Add, modify and remove Items

The shopping cart can contain any quantity of objects from different classes. Each item stored in the shopping cart is an instance of the sfShoppingCartItem class.

The sfShoppingCart class has addItem() and deleteItem() methods. As you can add or delete any type of object, the first argument of these method calls is the class name of the object.

To modify the quantity of one item, first get the sfShoppingCartItem object itself (via the getItems() method of the sfShoppingCart object) and call its setQuantity() method.

The shoppingcart module

Here is a possible module implementation of the shopping cart management where objects of class 'Product' (representing products) can be added, modified or suppressed with the actions 'add', 'update' and 'delete':

class shoppingcartActions extends sfActions
{
  // ...
 
  public function executeIndex()
  {
    $this->shopping_cart = $shopping_cart;
    $this->items = $shopping_cart->getItems();
 
    // ...
  }
 
  public function executeAdd($request)
  {
    // ...
 
    if ($request->hasParameter('id'))
    {
      $product = ProductPeer::retrieveByPk($request->getParameter('id'));
      $item = new sfShoppingCartItem('Product', $request->getParameter('id'));
      $item->setQuantity(1);
      $item->setPrice($product->getPrice());
      $shopping_cart = $this->getUser()->getShoppingCart();
      $shopping_cart->addItem($item);
    }
 
    // ...
  }
 
  public function executeUpdate($request)
  {
    $shopping_cart = $this->getUser()->getShoppingCart();
    foreach ($shopping_cart->getItems() as $item)
    {
      if ($request->hasParameter('quantity_'.$item->getId()))
      {
        $item->setQuantity($request->getParameter('quantity_'.$item->getId()));
      }
    }
 
    // ...
  }
 
  public function executeDelete($request)
  {
    if ($request->hasParameter('id'))
    {
      $shopping_cart = $this->getUser()->getShoppingCart();
      $shopping_cart->deleteItem('Product', $requets->getParameter('id'));
    }
 
    // ...
  }
 
  ...
}
 

Add an item

Let's take a closer look at this code.

To add an item to the shopping cart, you call the addItem() method, passing it a sfShoppingCartItem object. This object contains the object class and the unique id of the item to be added, the quantity to be added and the price of the item. This allows the shopping cart to contain objects of any class. For example, you could have a shopping cart containing books and CDs.

The price is stored at this moment to avoid difference of price between the product addition and the checkout if a product price is modified in between in a back-office (or if the cart can be kept between sessions). This also allows to apply price discount according to the amount ordered by the client:

if ($quantity > 10)
{
  $item->setPrice($product->getPrice() * 0.8);
}
else
{
  $item->setPrice($product->getPrice());
}
 

The problem is that you loose the original price if you apply the discount this way. That's why the the sfShoppingCartItem object has a setDiscount() method that expects a discount percentage:

if ($quantity > 10)
{
  $item->setPrice($product->getPrice());
  $item->setDiscount(20);
}
else
{
  $item->setPrice($product->getPrice());
}
 

Modify an item

To change the quantity of an item, use the method setQuantity() of the sfShoppingCartItem object. To delete an item, you can either call the deleteItem() method or change the quantity to 0 by calling setQuantity(0).

If a user adds the same item (same class and same id) several times, the shopping cart will increase the quantity of the item and not add a new one:

$item = new sfShoppingCartItem('Product', $request->getParameter('id'));
$item->setQuantity(1);
$item->setPrice($product->getPrice());
$shopping_cart = $this->getUser()->getShoppingCart();
$shopping_cart->addItem($item);
$shopping_cart->addItem($item);
 
// same as
 
$item = new sfShoppingCartItem('Product', $request->getParameter('id'));
$item->setQuantity(2);
$item->setPrice($product->getPrice());
$shopping_cart = $this->getUser()->getShoppingCart();
$shopping_cart->addItem($item);
 

Eventually, you may wonder why the update action uses arguments like 'quantity_2313=4' instead of 'id=2313&quantity=4'. As a matter of fact, the way this action is implemented allows the update of multiple article quantities at one time.

Delete an entire shopping cart

To reset the shopping cart, simply call the clear() method of the sfShoppingCart instance.

$this->getUser()->getShoppingCart()->clear();
 

Display the Shopping Cart in a Template

The action shoppingcart/index should display the content of the shopping cart. Let's examine a possible implementation.

Get the content of the shopping cart

Three methods of the sfShoppingCart object will help you get the content of a shopping cart:

Shopping cart items also have a parameter holder. This means that you can add custom information to any item.

For instance, in a website that sells auto parts, the sfShoppingCartItem objects need to store the objects added, but also the vehicle for which the part was bought. This can be simply done by adding:

$item->setParameter('vehicle', $vehicle_id);
 

you may need a getObjects() method instead of getItems(). This method exists but it relies on the Propel data access layer. As the use of Propel is optional, you might not be able to use it. Learn more about the data access layer in Chapter 8.

Pass the values to the template

In order to display the content of the shopping cart, the index action has to define a few variables accessible to the template:

// ...
 
$this->shopping_cart = $shopping_cart;
$this->items = $shopping_cart->getItems();
 

The following example shows a simple indexSuccess.php template based on an iteration over all the items of the shopping cart to display information about each of them:

<?php if ($shopping_cart->isEmpty()): ?>
 
  Your shopping cart is empty.
 
<?php else: ?>
 
  <?php foreach ($items as $item): ?>
    <?php $object = call_user_func(array($item->getClass().'Peer', 'retrieveByPK'), $item->getId()) ?> 
    <?php echo $object->getLabel() ?><br />
    <?php echo $item->getQuantity() ?><br />
    <?php echo currency_format($item->getPrice(), 'EUR' ) ?> 
    <?php if ($item->getDiscount()): >
       (- <?php echo $item->getDiscount() ?> %)
    <?php endif; ?><br />
  <?php endforeach; ?>
 
  Total : <?php echo currency_format($shopping_cart->getTotal(), 'EUR' ) ?><br />
 
<?php endif; ?>
 

Note that this example uses the Propel data access layer. If your project uses another data access layer, this example might need adaptations.

With or without Taxes

By default, all the operations (addition with $shopping_cart->addItem(), access with $item->getPrice() and $shopping_cart->getTotal() use prices without taxes.

To get the total amount with taxes, you have to call:

$total_with_taxes = $shopping_cart->getTotalWithTaxes()
 

If you need it, the sfShoppingCart object can be initialized so that the add and get methods use the price including taxes:

class myUser extends sfUser
{
  public function getShoppingCart()
  {
    if (!$this->hasAttribute('shopping_cart'))
    {
      $this->setAttribute('shopping_cart', new sfShoppingCart(sfConfig::get('app_cart_tax')));
    }
    $this->getAttribute('shopping_cart')->setUnitPriceWithTaxes(sfConfig::get('app_cart_withtaxes'));
 
    return $this->getAttribute('shopping_cart');
  }
 
  // ...
}
 

If sfConfig::get('app_cart_withtaxes') is set to true, the $shopping_cart->addItem() and $item->getPrice() methods will use prices with taxes. The getTotal() and getTotalWithTaxes() methods still give the correct results.

Once again, it is a good habit to keep the tax configuration in a configuration file: that's why the example above uses sfConfig::get('app_cart_withtaxes') instead of a simple true. The myproject/apps/myapp/config/app.yml should contain:

all:
  cart:
    tax:       19.6
    withtaxes: true
 

If you are unsure of the way taxes are handled, just ask the shopping cart:

$uses_tax = $shopping_cart->getUnitPriceWithTaxes();
 

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the user mailing-list or to the forum.