March 23, 2014

In this article, we are going to discuss about How to create the Drag and Drop shopping cart application using CodeIgniter Cart and ExtJS. We are using MYSQL for storing the product data. Here I am going to explain in step by step detail to create the Drag and Drop shopping cart app using CI cart and ExtJS.

Step 1 : Database Preparation

Create a database named "ci_extjs_cart" or with other name that you desired then create the table.

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 these are the sample datas (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');

Step 2 : CodeIgniter Configuration

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.

autoload.php

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

Step 3 : ExtJS configuration on CodeIgniter

Like always, include the ExtJS file on separate folder from Codeigniter (CI).

Step 4 : Controller and View creation

The Controller, create a 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.

Step 5 : 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');

Step 6 : 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

6.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

6.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.

6.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.

6.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.

6.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.



To download full source of this application Click Here

0 comments:

Post a Comment