ShoppingCart Plugin

  • Tags: ecommerce
  • Latest: 0.8.2
  • Last Updated: 16 March 2010
  • Grails version: 1.1 > *
  • Authors: null
11 votes
Dependency:
compile ":shopping-cart:0.8.2"

 Documentation

Summary

Installation

To install the shopping cart plugin please run this command from the root folder of your project:
grails install-plugin shopping-cart

The following artefacts will be added:

  • Groovy Source
    • IShoppable
    • SessionUtils
    • ShoppableMixin
  • Controllers
    • TestShoppingCartController
  • Domain
    • Quantity
    • Shoppable
    • ShoppingCart
    • ShoppingCartTestProduct
    • ShoppingCartInterfaceTestProduct
  • Services
    • ShoppingCartService
  • TagLib
    • ShoppingCartTagLib
  • Integration Tests
    • QuantityTests
    • ShoppingCartServiceTests
    • ShoppingCartTagLibTests
    • ShoppingCartTestProductTests
    • ShoppingCartInterfaceTestProductTests
    • ShoppingCartTests
    • TestShoppingCartControllerTests
All artefacts except for the view files belong to the com.metasieve.shoppingcart package

Description

Shopping Cart Plugin

Author: Björn Wilmsmann, MetaSieve

This plugin provides generic shopping cart functionality for Grails applications.

Usage

In order to use the plugin you can simply have a domain class (or several domain classes) extend the new com.metasieve.shoppingcart.Shoppable entity:

class Game extends com.metasieve.shoppingcart.Shoppable {
   …
}

class Book extends com.metasieve.shoppingcart.Shoppable { … }

If your application sports an inheritance hierarchy that would conflict with another class to inherit from, there is another way of using the plugin. If, for instance, you have something like these domain classes

class Content {
   …
}

class Download extends Content { … }

class Movie extends Content { … }

class NotForSale extends Content { … }

the approach described above won't work since Java doesn't support multiple inheritance. Although in this case you could have Content extend Shoppable, this approach is kind of ugly, especially if there are sub-classes (like NotForSale in the example above) which are not meant to be put in a shopping cart.

However, there is another more elegant way of accommodating such an inheritance hierarchy. Instead of having classes extend Shoppable you can also implement the com.metasieve.shoppingcart.IShoppable interface and add another field named shoppingItem of type com.metasieve.shoppingcart.ShoppingItem to the domain class:

class Content {
   …
}

class Download extends Content implements com.metasieve.shoppingcart.IShoppable { com.metasieve.shoppingcart.ShoppingItem shoppingItem … }

class Movie extends Content implements com.metasieve.shoppingcart.IShoppable { com.metasieve.shoppingcart.ShoppingItem shoppingItem … }

class NotForSale extends Content { … }

The only downside of the latter approach is that you cannot simply treat all shopping cart items as instances of Shoppable (for example when listing them) but you have to implement your own logic for treating the different classes that implement IShoppable.

During application start a few methods will be magically added to the domain classes that either extend Shoppable or implement IShoppable:

addToShoppingCart()
addQuantityToShoppingCart(Integer qty)
removeFromShoppingCart()
removeQuantityFromShoppingCart(Integer qty)

Adding To And Removing From The Shopping Cart

If, for example, you want to add an item of type Game to your shopping cart, you would simply do:
def game = new Game(...)
game.addToShoppingCart()

Conversely, if you want to remove that game again, you can do:

game.removeFromShoppingCart()

If you do not want to add or remove just a single item but several items (let's say 3) of the same kind you can use:

game.addQuantityToShoppingCart(3)
and
game.removeQuantityFromShoppingCart(3)

Another way of achieving the same result of course is doing something like:

3.times {
   game.addToShoppingCart()
}
However, for large quantities the latter is prone to having a negative impact on performance.

Alternative: Using ShoppingCartService

Instead of using the domain class methods you can also use the corresponding methods of the ShoppingCartService. To do so, you have to inject the service as usual:
def shoppingCartService

Afterwards, you can do something like:

def game = new Game(...)
def qty = 3

shoppingCartService.addToShoppingCart(game, qty) shoppingCartService.removeFromShoppingCart(game, qty)

shoppingCartService.addToShoppingCart(game) // qty defaults to 1 shoppingCartService.removeFromShoppingCart( game) // qty defaults to 1

Please note that there is an additional argument for each method telling the service which item is supposed to be added or removed. Moreover, if no third argument is supplied to these methods, the quantity added or removed will default to 1.

Checking Out And Emptying The Shopping Cart

In order to check out, all you need to do is inject the ShoppingCartService and call the checkOut(n) method of that service:
def shoppingCartService
…
def checkedOutItems = shoppingCartService.checkOut()

checkedOutItems.each { println it['item'] println it['qty'] }

it['item'] in the example above will be an entity of a domain class that extends Shoppable while it['qty'] will be an Integer telling you how many items of that entity have been added to the shopping cart until checkout.

checkOut() also sets the checkedOut property of the current user's shopping cart to true, thus detaching it from the session but keeping it for later examination (i.e. data mining).

The shoppingCartService service provides an emptyShoppingCart() method as well, which simply removes all items from the shopping cart without checking out:

def shoppingCartService
…
shoppingCartService.emptyShoppingCart()

Additional Service Methods

Finally, the ShoppingCartService also defines the following methods:
setLastURL(def url) 
getItems()
getQuantity(IShoppable product)
setLastURL(def url) can be used to set the current URL for the session, which is useful if you want to track where a user has abandonned a shopping cart.
getItems() yields the current items of the shopping cart associated with the session.
Finally, getQuantity(IShoppable product) will return the current quantity of the product in the shopping cart associated with the session.

TagLib

A tag for easily iterating over the current items in a shopping cart is provided, too:
<table>
   <sc:each>
      <tr>
         <td>
            ${com.metasieve.shoppingcart.Shoppable.findByShoppingItem(it['item'])}
         </td>
         <td>
            ${it['qty']}
         </td>
         …
      </tr>
   </sc:each>
</table>

Payment via PayPal

Using the Grails PayPal plugin you can easily add payment functionality to your shopping cart as well:
import org.grails.paypal.Payment

class ShoppingCartPayment { com.metasieve.shoppingcart.ShoppingCart shoppingCart Payment payment Boolean payed = false

def commit() { if (!payed) { shoppingCart.items.each { item -> … }

// detach shopping cart shoppingCart.checkedOut = true shoppingCart.save()

payed = true save() } } }

Persisting Shopping Cart Across Sessions

By append WithSessionID to each method name you can also use an additional session ID attribute when adding to or removing from a shopping cart, which comes in handy if you want to persist a shopping cart across browser sessions:
def qty = params.quantity
def previousSessionID = Session.findByCookieID(cookieID)

...

addToShoppingCartWithSessionID(previousSessionID) removeFromShoppingCartWithSessionID(previousSessionID) addQuantityToShoppingCartWithSessionID(qty, previousSessionID) removeQuantityFromShoppingCartWithSessionID(qty, previousSessionID)

If you use shoppingCartService for adding to and removing from shopping carts you can use shoppingCartService.getShoppingCart(def previousSessionID) in order to retrieve a shopping cart from a previous session. You can then use this shopping cart as an additional optional argument with the usual service methods like this:

def product = Product.get(params.id)
def qty = params.quantity
def previousSessionID = Session.findByCookieID(cookieID)
def shoppingCart = shoppingCartService.getShoppingCart(previousSessionID)

...

shoppingCartService.addToShoppingCart(product, qty, shoppingCart) shoppingCartService.removeFromShoppingCart(product, qty, shoppingCart) shoppingCartService.addToShoppingCart(product, shoppingCart) shoppingCartService.removeFromShoppingCart(product, shoppingCart) setLastURL(url, shoppingCart) def items = getItems(shoppingCart) def someQty = getQuantity(product, shoppingCart)

Further Examples

For further examples please have a look at the source code in:
  • $PLUGIN_DIR/shopping-cart-$VERSION/grails-app/controllers/com/metasieve/shoppingcart/TestShoppingCartController.groovy
  • $PLUGIN_DIR/shopping-cart-$VERSION/grails-app/views/testShoppingCart/_shoppingCartContent.gsp
  • $PLUGIN_DIR/shopping-cart-$VERSION/grails-app/views/testShoppingCart/show.gsp

Plugin version history

See http://jira.codehaus.org for more information