Drag and Drop Shopping Cart Using ExtJS and CodeIgniter Cart

August 21st, 2010 by aditia rahman / 10 Comments  

     

This is my other post about simple usage of ExtJS with CodeIgniter, this time I want to create a simple drag and drop shopping cart, I have learn about how to use the cart class in CodeIgniter that I have posted in the previous post, i’m using MySQL for the storing the product data. I prepared the mockup it something like this

Let just get started to the code:

1. Prepare The Database

Create table named “ci_extjs_cart” or with other name that you desired, next create the table and here the sql code

CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) DEFAULT NULL,
  `price` varchar(32) DEFAULT NULL,
  `image` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

And this are the sample data (I already included the sample image on the code that you can download)

INSERT INTO `products` (`id`, `name`, `price`, `image`) VALUES
(1, 'HP - 20 Inch Widescreen Flat-Panel LCD Monitor', '169', 'hp.jpg'),
(2, 'Gateway - 20 Inch Widescreen Flat-Panel LCD Monitor', '159', 'gateway.jpg'),
(3, 'Apple - 30 Flat-Panel TFT-LCD Monitor', '1799', 'apple.jpg'),
(4, 'Acer - 24 Inch Flat-Panel LED-LCD Monitor', '299', 'acer.jpg'),
(5, 'Asus - 24 Inch Widescreen Flat-Panel LCD Monitor', '249', 'asus.jpg');

2. Setting Up CodeIgniter

First time to get hand dirty with a framework is to set all the config file (in folder: “application/config/”). The four files that we have to configure are:

config.php

$config['base_url']	= "http://localhost/ci_extjs_cart/";

database.php

$db['default']['hostname'] = "localhost";
$db['default']['username'] = "root";
$db['default']['password'] = "admin";
$db['default']['database'] = "ci_extjs_cart";
$db['default']['dbdriver'] = "mysql";

routes.php

$route['default_controller'] = "product";

The default controller is set to product controller, in this step we have not create the controller yet, but it will be.

autoload.php

$autoload['libraries'] = array('database', 'session');
$autoload['helper'] = array('url');

4. Setting Up ExtJS on CodeIgniter

Like always I always included ExtJS file on separate folder from Codeigniter

5. Create a Controller and a View

The Controller, create file product.php inside the controllers folder, the very usual method on the controller is the contrustion and the index method, in the construction method the codeigniter cart class loaded.

<?php

class Product extends Controller {

    public function  __construct()
    {
        parent::Controller();
        $this->load->library('cart');
    }

    public function index()
    {
        $this->load->view('product/index');
    }
}

The View, create the view file inside views folder “index.php”, the first step to preparing the view is to preparing the HTML layout, include the ExtJS file, and create a basic layout style in CSS.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="<?php echo base_url(); ?>assets/js/ext/resources/css/ext-all.css"/>
    <script type="text/javascript" src="<?php echo base_url(); ?>assets/js/ext/adapter/ext/ext-base.js"></script>
    <script type="text/javascript" src="<?php echo base_url(); ?>assets/js/ext/ext-all.js"></script>
    <!-- product data view style -->
    <link rel="stylesheet" type="text/css" href="<?php echo base_url(); ?>assets/js/ext/ux/data-view.css"/>

    <script type="text/javascript">
    var BASE_URL = '<?php echo site_url(); ?>/';

    Ext.onReady(function() {
    });
    </script>
    <title>Extjs Image Gallery Using DataView</title>
    <style type="text/css">
        body {
            padding: 20px;
            margin: 0 auto;
        }
        #container {
            padding: 10px;
            background: #e3e3e3;
            border: 1px solid #d3d3d3;
            margin: 0 auto;
            text-align: left;
            width: 630px;
        }
        #top {

        }
        #bottom {
            margin-top: 10px;
        }
    </style>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
      <div id="container">
          <div id="top"></div>
          <div id="bottom"></div>
      </div>
  </body>
</html>

The Ext.ready function is still empty, inside this function all the ExtJS component will be placed, and I always using BASE_URL value that have value of site_url(); function, to make it easier when accessing a url.

6. Get Product List

The Controller, this is the method the get all the product data, and sending the data as json format.

public function ext_get_all()
{
    $query = $this->db->get('products');
    $product_arr = array();
    foreach($query->result() as $key => $data)
    {
        $product_arr[] = array(
            'id'    => $data->id,
            'name'  => $data->name,
            'price' => $data->price,
            'image' => $data->image
        );
    }
    echo json_encode(array('products' => $product_arr));
}

The View, this is the code for creating the product list in dataview, and a function to make the dataview draggable.

var proxyProduct = new Ext.data.HttpProxy({
    url: BASE_URL + 'product/ext_get_all', method: 'POST'
});

var strProduct = new Ext.data.JsonStore({
    proxy: proxyProduct,
    root: 'products',
    fields: [
        'id', 'name', 'price', 'image'
    ]
});

strProduct.load();

var tplProduct = new Ext.XTemplate(
    '<tpl for=".">',
        '<div class="thumb-wrap" id="{name}">',
        '<div class="thumb"><img src="http://localhost/ci_extjs_cart/assets/img/{image}" title="{name}"></div>',
        '<span class="name">{name}</span>',
        '<div class="price">$ {price}</div></div>',
    '</tpl>',
    '<div class="x-clear"></div>'
);

var dvProduct = new Ext.DataView({
    autoScroll: true, store: strProduct, tpl: tplProduct,
    autoHeight: false, height: 200, multiSelect: false,
    overClass: 'x-view-over', itemSelector: 'div.thumb-wrap',
    emptyText: 'No product to display',
    style: 'border:1px solid #99BBE8;',
    listeners: {
        render: initializeItemDragZone
    }
});

function initializeItemDragZone(v) {
    v.dragZone = new Ext.dd.DragZone(v.getEl(), {
        getDragData: function(e) {
            var sourceEl = e.getTarget(v.itemSelector, 10);
            if (sourceEl) {
                d = sourceEl.cloneNode(true);
                d.id = Ext.id();
                return v.dragData = {
                    sourceEl: sourceEl,
                    repairXY: Ext.fly(sourceEl).getXY(),
                    ddel: d,
                    itemData: v.getRecord(sourceEl).data
                }
            }
        },

        getRepairXY: function() {
            return this.dragData.repairXY;
        }
    });
}

var panelProduct = new Ext.Panel({
    id: 'images-view',
    frame: true,
    width: 620,
    autoHeight: true,
    title: 'Product DataView',
    style: 'margin:0 auto;',
    items: [dvProduct]
});

panelProduct.render('top');

7. Create The Cart (Using EditorGridPanel)

The Controller, we will create some basic function to manipulating the cart I try to explain them in the separate way, but still in the same controller

7.1 Get All Cart

public function ext_get_cart()
{
    if ($this->cart->total_items() != 0)
    {
        foreach($this->cart->contents() as $product)
        {
            $cart_arr[] = array(
                'rowid' => $product['rowid'],
                'id'    => $product['id'],
                'qty'   => $product['qty'],
                'name'  => $product['name'],
                'price' => $product['price'],
                'subtotal' => $product['subtotal']
            );
        }
        $cart_arr[] = array(
            'rowid' => '',
            'id'    => '',
            'qty'   => '',
            'name'  => '',
            'price' => '<b>Total:</b>',
            'subtotal' => '<b>'.$this->cart->total().'</b>'
        );
        echo json_encode(array('cart' => $cart_arr));
    }
    else
    {
        $cart_arr[] = array();
        echo json_encode(array('cart' => $cart_arr));
    }
}

This function is to getting all the available product in the cart in the session, if the cart is not empty then just loop through the cart function “$this->cart->contents()” and put this to array. I add another array to showing the total value from the cart, this will be treated different in the ExtJS grid, and send it as json format

7.2 Add a Product to Cart

public function ext_add_cart()
{
    if ($_POST['rowid'] == '')
    {
        $data = array(
            'id'    => $_POST['id'],
            'qty'   => 1,
            'price' => $_POST['price'],
            'name'  => $_POST['name']
        );
        $this->cart->insert($data);
    }
    else
    {
        $data = array(
          'rowid'   => $_POST['rowid'],
          'qty'     => intval($_POST['qty']) + 1
        );
        $this->cart->update($data);
    }
    echo '{success:true, total: "'.$this->cart->total().'"}';
}

You must be can see in the add cart function there are conditional statement that updating the cart instead insert a new data to cart, well this is to handle if a user adding the product that already available on the cart, so the cart must be updating only the quantity.

7.3 Update a Product Quantity

public function ext_update_cart()
{
    $data = array(
      'rowid'   => $_POST['rowid'],
      'qty'     => $_POST['qty']
    );
    $this->cart->update($data);
    echo '{success:true}';
}

This function is to updating a product quantity on the cart, I make the quantity editable on the grid so the user can easy put the number of a product that he desired, and to deleting a product from cart the user have to put zero value on the quantity editable grid.

7.4 Clear Cart

public function ext_clear_cart()
{
    $this->cart->destroy();
    echo '{success:true}';
}

Well nothing really hard in this function, using “$this->cart->destroy()” all the cart clear.

7.5 The Cart

var strCart = new Ext.data.JsonStore({
    root: 'cart',
    fields: [
        'rowid', 'id', 'qty', 'name', 'price', 'subtotal'
    ],
    proxy: new Ext.data.HttpProxy({
        url: BASE_URL + 'product/ext_get_cart', method: 'POST'
    })
});
strCart.load();

var cb_select = new Ext.grid.CheckboxSelectionModel();

function showDollar(val) {
    if (val == '' || val == '<b>Total:</b>') {
        return val;
    }
    else {
        return '$ ' + val;
    }
}

var panelCart = new Ext.grid.EditorGridPanel({
    frame: true, border: true, stripeRows: true,
    store: strCart, loadMask: true, title: 'Your Cart (Drag Here)',
    style: 'margin:0 auto;font-size:13px;',
    height: 220, width: 500, sm: cb_select,
    columns: [{
        header: 'rowid',
        dataIndex: 'rowid',
        hidden: true,
        hideable: false
    }, {
        header: 'id',
        dataIndex: 'id',
        hidden: true,
        hideable: false
    }, {
        header: "Product Name",
        dataIndex: 'name',
        sortable: true,
        width: 280
    }, {
        header: "Qty",
        align: 'center',
        width: 40,
        dataIndex: 'qty',
        menuDisabled: true,
        editor: new Ext.form.TextField({
            allowBlank: false,
            vtype: 'alphanum',
            id: 'qtyField'
        })
    }, {
        header: "Price",
        dataIndex: 'price',
        sortable: true,
        align: 'right',
        renderer: showDollar,
        width: 80
    }, {
        header: "Subtotal",
        sortable: true,
        align: 'right',
        width: 80,
        renderer: showDollar
    }],
    listeners: {
        'afteredit': function() {
            var sm = panelCart.getSelectionModel().getSelections();
            panelCart.getSelectionModel().clearSelections();
            Ext.Ajax.request({
                method: 'POST',
                url: BASE_URL + 'product/ext_update_cart',
                params: {
                    rowid: sm[0].get('rowid'),
                    qty: Ext.getCmp('qtyField').getValue()
                },
                success: function() {
                    strCart.load();
                }
            });
        }
    },
    buttons: [{
        text: 'Clear Cart',
        handler: function() {
            Ext.Ajax.request({
                url: BASE_URL + 'product/ext_clear_cart',
                method: 'POST',
                success: function() {
                    strCart.load();
                }
            });
        }
    }, {
        text: 'Check Out',
        handler: function() {

        }
    }]
});

panelCart.render('bottom');

var formPanelDropTargetEl =  panelCart.getView().scroller.dom;

var formPanelDropTarget = new Ext.dd.DropTarget(formPanelDropTargetEl, {
    notifyDrop  : function(ddSource, e, data){
        panelCart.getSelectionModel().selectAll();
        var sm = panelCart.getSelectionModel().getSelections();
        panelCart.getSelectionModel().clearSelections();

        data.itemData.rowid = '';
        for (i=0; i<=sm.length-1; i++) {
            if (sm[i].get('id') == data.itemData.id) {
                data.itemData.rowid = sm[i].get('rowid');
                data.itemData.qty = sm[i].get('qty');
                // so can out from loop
                i = sm.length;
            }
        }

        Ext.Ajax.request({
            url: BASE_URL + 'product/ext_add_cart',
            method: 'POST',
            params: {
                'id': data.itemData.id,
                'price': data.itemData.price,
                'name': data.itemData.name,
                'rowid': data.itemData.rowid,
                'qty': data.itemData.qty
            },
            success: function() {
                strCart.load();
            }
        });
        return(true);
    }
});

That’s all for the Cart features, there is CheckboxSelectionModel component, this component is to check whether the product is already on the cart, you can see it used on notifyDrop inside the dropTarget you have to add, the expected result is something like this.

ExtJS & CodeIgniter Cart

Download Source

        submit to reddit Delicious

10 Comments Leave a Comment Subscribe RSS

  • Julien says:

    Hello,

    thanks a lot for the example. I tried to reproduce this example but when i’m trying to access to the scroller object of the GridView, i’m getting an error :

    var formPanelDropTargetEl = test.getView().scroller.dom;

    When i m using console.log of test.getView(), the scroller attribute is displayed and i can explore this object but there is no way to access to the scroller object.

    Am i doing something wrong ?

    Thanks for any suggestion.

    P.s : Using ExtJS 3.2.1

  • diego says:

    Hi . nice tutorial ..
    i need to know how to make the check out to work ? and also how to make the shopping cart separate for different clients .
    thank you and i hope to get an anwser :)

  • Mike says:

    Great stuff! I find myself coming back to your site almost daily!

    I’m trying to get this demo into a viewport. I’d like to have a tree in the west, your panelProduct in the center and panelCart in the south, but I was getting errors related to the scroll.dom

    Just wondering if you think I can put this in a viewport without much effort in changing the code. I don’t want to break anything!

  • Sajith says:

    How to create category on the cart

  • Subho says:

    How to add multiple category product on this same cart cart.

Leave a Comment