ドラッグ&ドロップカート

ドラッグ&ドロップで商品が選べる楽しい機能

 買い物はやはり楽しくないといけないですね。そんなわけで、商品一覧で商品の画像をドラッグしてカートでドロップすると、カートに入るという機能を実装してみました。

 サンプルサイトはこちらです。http://ec-cube.sfriend.ne.jp/dd-cart/products/list.php

ddcart

 こんな風にドラッグして、カゴにドロップします。これをベースにいろいろ面白いサイトが作れそうではありませんか!

実装方法
JQueryを使ってますので、以下からダウンロードしてください。
http://jqueryui.com/download
以下の3つをチェックしてダウンロード。
(2009.9.4時点: jQueryUI 1.7.2)

・UI Core
・Draggable
・Droppable

html/user_data/packages/default/js/jquery-ui
作成したディレクトリに、以下の2つのダウンロードしたjQueryのファイル(最小化されたもの)を置いてください。
・jquery-1.3.2.min.js
・jquery-ui-1.7.2.custom.min.js

以下のファイルを変更しています。
[変更] data/class/pages/frontparts/bloc/LC_Page_FrontParts_Bloc_Cart.php
function process()を以下のように変更。

    function process() {
        $objSubView = new SC_SiteView();
        $objCart = new SC_CartSession();
        $objSiteInfo = new SC_SiteInfo;

        if (count($_SESSION[$objCart->key]) > 0){
            // カート情報を取得
            $arrCartList = $objCart->getCartList();

            // カート内の商品ID一覧を取得
            $arrAllProductID = $objCart->getAllProductID();
            // 商品が1つ以上入っている場合には商品名称を取得
            if (count($arrAllProductID) > 0){
                $objQuery = new SC_Query();
                $arrVal = array();
                $sql = "";
                $sql = "SELECT name FROM dtb_products WHERE product_id IN ( ?";
                $arrVal = array($arrAllProductID[0]);
                for($i = 1 ; $i < count($arrAllProductID) ; $i++){
                    $sql.= " ,? ";
                    array_push($arrVal, $arrAllProductID[$i]);
                }
                $sql.= " )";

                $arrProduct_name = $objQuery->getAll($sql, $arrVal);

                foreach($arrProduct_name as $key => $val){
                    $arrCartList[$key]['product_name'] = $val['name'];
                }
            }
            
	        // カート内容のサムネイル表示のため(ここで合計金額の更新もされる)
	        $arrInfo = $objSiteInfo->data;
	        $db = new SC_Helper_DB_Ex();
	        $db->sfTotalCart($this, $objCart, $arrInfo);
	        
            // 店舗情報の取得
            $arrInfo = $objSiteInfo->data;
            // 購入金額合計
            $ProductsTotal = $objCart->getAllProductsTotal($arrInfo);

            // 合計個数
            $TotalQuantity = $objCart->getTotalQuantity();

            // 送料無料までの金額
            $arrCartList[0]['ProductsTotal'] = $ProductsTotal;
            $arrCartList[0]['TotalQuantity'] = $TotalQuantity;
            $deliv_free = $arrInfo['free_rule'] - $ProductsTotal;
            $arrCartList[0]['free_rule'] = $arrInfo['free_rule'];
            $arrCartList[0]['deliv_free'] = $deliv_free;

            $this->arrCartList = $arrCartList;
        }

        $objSubView->assignobj($this);
        $objSubView->display($this->tpl_mainpage);
    }

[変更] data/class/pages/products/LC_Page_Products_List.php
function process()を以下のように変更。

    function process() {
        $objView = new SC_SiteView();
        $conn = new SC_DBConn();
        $objDb = new SC_Helper_DB_Ex();

        // レイアウトデザインを取得
        $helper = new SC_Helper_PageLayout_Ex();
        $helper->sfGetPageLayout($this, false, DEF_LAYOUT);

        //表示件数の選択
        if(isset($_POST['disp_number'])
           && SC_Utils_Ex::sfIsInt($_POST['disp_number'])) {
            $this->disp_number = $_POST['disp_number'];
        } else {
            //最小表示件数を選択
            $this->disp_number = current(array_keys($this->arrPRODUCTLISTMAX));
        }

        //表示順序の保存
        $this->orderby = isset($_POST['orderby']) ? $_POST['orderby'] : "";

        // GETのカテゴリIDを元に正しいカテゴリIDを取得する。
        $arrCategory_id = $objDb->sfGetCategoryId("", $_GET['category_id']);

        if (!isset($_GET['mode'])) $_GET['mode'] = "";
        if (!isset($_GET['name'])) $_GET['name'] = "";
        if (!isset($_POST['orderby'])) $_POST['orderby'] = "";
        if (empty($arrCategory_id)) $arrCategory_id = array("0");

        // タイトル編集
        $tpl_subtitle = "";
        if ($_GET['mode'] == 'search') {
            $tpl_subtitle = "検索結果";
        } elseif (empty($arrCategory_id[0])) {
            $tpl_subtitle = "全商品";
        } else {
            $arrFirstCat = $objDb->sfGetFirstCat($arrCategory_id[0]);
            $tpl_subtitle = $arrFirstCat['name'];
        }

        $objQuery = new SC_Query();
        $count = $objQuery->count("dtb_best_products", "category_id = ?", $arrCategory_id);

        // 以下の条件でBEST商品を表示する
        // ・BEST最大数の商品が登録されている。
        // ・カテゴリIDがルートIDである。
        // ・検索モードでない。
        if(($count >= BEST_MIN) && $this->lfIsRootCategory($arrCategory_id[0]) && ($_GET['mode'] != 'search') ) {
            // 商品TOPの表示処理
            $this->arrBestItems = SC_Utils_Ex::sfGetBestProducts($conn, $arrCategory_id[0]);
            $this->BEST_ROOP_MAX = ceil((BEST_MAX-1)/2);
        } else {
            if ($_GET['mode'] == 'search' && strlen($_GET['category_id']) == 0 ){
                // 検索時にcategory_idがGETに存在しない場合は、仮に埋めたIDを空白に戻す
                $arrCategory_id = array(0);
            }

            // 商品一覧の表示処理
            $this->lfDispProductsList($arrCategory_id[0], $_GET['name'], $this->disp_number, $_POST['orderby']);

            // 検索条件を画面に表示
            // カテゴリー検索条件
            if (strlen($_GET['category_id']) == 0) {
                $arrSearch['category'] = "指定なし";
            }else{
                $arrCat = $conn->getOne("SELECT category_name FROM dtb_category WHERE category_id = ?", $arrCategory_id);
                $arrSearch['category'] = $arrCat;
            }

            // 商品名検索条件
            if ($_GET['name'] === "") {
                $arrSearch['name'] = "指定なし";
            }else{
                $arrSearch['name'] = $_GET['name'];
            }
        }

        // レイアウトデザインを取得
        $layout = new SC_Helper_PageLayout_Ex();
        $layout->sfGetPageLayout($this, false, "products/list.php");

        if(isset($_POST['mode']) && ($_POST['mode'] == "cart" || $_POST['mode'] == "dd-cart")
           && $_POST['product_id'] != "") {

            // 値の正当性チェック
            if(!SC_Utils_Ex::sfIsInt($_POST['product_id']) || !$objDb->sfIsRecord("dtb_products", "product_id", $_POST['product_id'], "del_flg = 0 AND status = 1")) {
                SC_Utils_Ex::sfDispSiteError(PRODUCT_NOT_FOUND);
            } else {
                // 入力値の変換
                $this->arrErr = $this->lfCheckError($_POST['product_id']);
                if(count($this->arrErr) == 0) {
                    $objCartSess = new SC_CartSession();
                    $classcategory_id = "classcategory_id". $_POST['product_id'];
                    $classcategory_id1 = $_POST[$classcategory_id. '_1'];
                    $classcategory_id2 = $_POST[$classcategory_id. '_2'];
                    $quantity = "quantity". $_POST['product_id'];
                    // 規格1が設定されていない場合
                    if(!$this->tpl_classcat_find1[$_POST['product_id']]) {
                        $classcategory_id1 = '0';
                    }
                    // 規格2が設定されていない場合
                    if(!$this->tpl_classcat_find2[$_POST['product_id']]) {
                        $classcategory_id2 = '0';
                    }
                    $objCartSess->setPrevURL($_SERVER['REQUEST_URI']);
                    $objCartSess->addProduct(array($_POST['product_id'], $classcategory_id1, $classcategory_id2), $_POST[$quantity]);
                    
                    if($_POST['mode'] == "cart"){
	                    $this->sendRedirect($this->getLocation(URL_CART_TOP));
	                    exit;
                    }else if($_POST['mode'] == "dd-cart"){
                        // もともと表示されていた画面にリダイレクトする
                        $this->lfRedirectRequestUrl(); 
                    }
                }
            }
        }
        
        // ドラッグ&ドロップでのカートからの削除
        if(isset($_POST['mode']) && $_POST['mode'] == "dd-delete" && $_POST['cart_no'] != "")
        {
        	$objCartSess = new SC_CartSession();
        	$objCartSess->delProduct($_POST['cart_no']);
        	
        	// もともと表示されていた画面にリダイレクトする
        	$this->lfRedirectRequestUrl();
        }

        $this->tpl_subtitle = $tpl_subtitle;

        // 支払方法の取得
        $this->arrPayment = $this->lfGetPayment();
        // 入力情報を渡す
        $this->arrForm = $_POST;

        $this->lfConvertParam();

        $this->category_id = $arrCategory_id[0];
        $this->arrSearch = $arrSearch;

        $objView->assignobj($this);
        $objView->display(SITE_FRAME);
    }

最後の方に以下の関数を追加。

    function lfRedirectRequestUrl() {
        $dd_request_url = urldecode($_POST['dd-request-url']); 
        header("Location: " . $dd_request_url);
        exit();
    }

[変更] data/Smarty/templates/default/bloc/cart.tpl
以下のように変更します。

<!-- ▼DD-Cart -->
<style type="text/css">
.in-cart-product {
  margin-top:2px;
  cursor: move;
}

.dropped-hover {
  background-color: #ffebca;
}
</style>

  <form name="dd-cart-add-form" id="dd-cart-add-form" method="post" action="<!--{$smarty.const.URL_DIR}-->products/list.php">
    <input type="hidden" name="mode" value="dd-cart" />
    <input type="hidden" name="product_id" id="dd-cart-add-form-product_id" value="" />
    
    <input type="hidden" name="dd-request-url" value="<!--{$smarty.server.REQUEST_URI|escape}-->" />
  </form>

  <form name="dd-form" id="dd-form" action="<!--{$smarty.const.URL_DIR}-->products/list.php" method="post">
    <input type="hidden" name="mode" value="dd-delete" />
    <input type="hidden" name="cart_no" id="dd-form-cart-no" value="" />
    <input type="hidden" name="dd-request-url" value="<!--{$smarty.server.REQUEST_URI|escape}-->" />
  </form>

<!-- ▲DD-Cart -->

<!--現在のカゴの中ここから-->
  <h2>
    <img src="<!--{$TPL_DIR}-->img/side/title_cartin.jpg" width="166" height="35" alt="現在のカゴの中" />
  </h2>
  
<div id="dd-cartarea">
  <div id="cartarea">
    <p class="item">商品数:<!--{$arrCartList.0.TotalQuantity|number_format|default:0}-->点</p>
    <p>合計:<span class="price"><!--{$arrCartList.0.ProductsTotal|number_format|default:0}-->円</span><br />
    <!-- カゴの中に商品がある場合にのみ表示 -->
    <!--{if $arrCartList.0.TotalQuantity > 0 and $arrCartList.0.free_rule > 0}-->
      <!--{if $arrCartList.0.deliv_free > 0}-->
      送料手数料無料まであと<!--{$arrCartList.0.deliv_free|number_format|default:0}-->円(税込)です。
      <!--{else}-->
      現在、送料は「<span class="price">無料</span>」です。
      <!--{/if}-->
    <!--{/if}-->
    </p>
    <p class="btn">
      <a href="<!--{$smarty.const.URL_DIR}-->cart/index.php" onmouseover="chgImg('<!--{$TPL_DIR}-->img/side/button_cartin_on.gif','button_cartin');" onmouseout="chgImg('<!--{$TPL_DIR}-->img/side/button_cartin.gif','button_cartin');">
        <img src="<!--{$TPL_DIR}-->img/side/button_cartin.gif" width="87" height="22" alt="カゴの中を見る" border="0" name="button_cartin" id="button_cartin" />
      </a>
     </p>
     
     <!-- ▼DD-Cart -->
     <!--{section name=cnt loop=$arrProductsClass}-->
     <div class="in-cart-product">
       <img src="<!--{$smarty.const.URL_DIR}-->resize_image.php?image=<!--{$arrProductsClass[cnt].main_list_image}-->&width=65&height=65" alt="<!--{$arrProductsClass[cnt].name|escape}-->" style="float:left;margin-right:1px;" />
       <span>
         個数:<!--{$arrProductsClass[cnt].quantity}--><br />
         <!--{if $arrProductsClass[cnt].classcategory_name1 != ""}-->
           <!--{$arrProductsClass[cnt].class_name1}-->:<!--{$arrProductsClass[cnt].classcategory_name1}--><br />
         <!--{/if}-->
         <!--{if $arrProductsClass[cnt].classcategory_name2 != ""}-->
           <!--{$arrProductsClass[cnt].class_name2}-->:<!--{$arrProductsClass[cnt].classcategory_name2}-->
         <!--{/if}-->
       </span>
       <span class="cart-no" style="display:none;"><!--{$arrProductsClass[cnt].cart_no}--></span>
       <br style="clear:both;" />
     </div>
     <!--{/section}-->
     
     <p id="cartover" style="display:none;">この上でドロップするとカートに追加できます</p>
     <!-- ▲DD-Cart -->
  </div>
</div>
<!--現在のカゴの中ここまで-->

<!-- ▼DD-Cart -->
<script type="text/javascript">
// カートに追加
$('#dd-cartarea').droppable({
  accept: '.draggable-product',
  tolerance: 'intersect',	// 半分以上入れば、上に乗ったと判定する
  hoverClass: "dropped-hover",
  greedy: true,
  drop: function(ev, ui) {
    // ■値の取得
    // アイテムIDを取得
    var id = $(ui.draggable).attr('itemid');
    // 数量
    var quantity = $(ui.helper).attr('quantity');
    // 規格1
    var class1 = $(ui.helper).attr('class1');
    // 規格2
    var class2 = $(ui.helper).attr('class2');
    
    // ■チェック
    // 数量
    if(quantity == undefined || isNaN(quantity) || quantity <= 0){
      return false;
    }
    // 規格1
    if(class1 != undefined && class1 == ''){
      return false;
    }
    // 規格2
    if(class2 != undefined && class2 == ''){
      return false;
    }
    
    // ■値のセット
    // アイテムID
    $('#dd-cart-add-form-product_id').val(id);
    // 数量
    var inputQuantity = $("<input type='hidden' name='quantity" + id + "' value='" + quantity + "' />");
    $('#dd-cart-add-form').append(inputQuantity);
    // 規格1
    var inputClass1 = $("<input type='hidden' name='classcategory_id" + id + "_1' value='" + class1 + "' />");
    $('#dd-cart-add-form').append(inputClass1);
    // 規格2
    var inputClass2 = $("<input type='hidden' name='classcategory_id" + id + "_2' value='" + class2 + "' />");
    $('#dd-cart-add-form').append(inputClass2);
    
    // ■送信
    $('#dd-cart-add-form').submit();
  },
  over: function(ev, ui) {
    // Draggable要素が上に乗ったときに文字を表示(そして、フェードアウト)
    $("#cartover", this).show().fadeOut(5000);
  },
  out: function(ev, ui) {
    // Draggable要素が外に出たときに文字を非表示
    $("#cartover", this).hide();
  }
});

// カートから削除
$('.in-cart-product').draggable({
  containment: 'document',
  helper: 'original',
  revert: 'invalid',
  opacity: 0.8,
  cursor: 'move',
  zIndex: 10,
});

$('#documentarea').droppable({
  accept: '.in-cart-product',
  greedy: true,
  drop: function(ev, ui) {
    var cartno = $(ui.draggable).children(".cart-no").text();
    $('#dd-form-cart-no').val(cartno);
    $('#dd-form').submit();
  }
});

</script>
<!-- ▲DD-Cart -->

[変更] data/Smarty/templates/default/list.tpl
formタグ(form1)のhidden(product_id)の下に以下を追加。

    <input type="hidden" name="dd-request-url" value="<!--{$smarty.server.REQUEST_URI|escape}-->" />

    <!--▼商品ここから-->
    <div class="listarea">
      <div class="listphoto">
        <!--★画像★-->
        <a href="<!--{$smarty.const.DETAIL_P_HTML}--><!--{$arrProducts[cnt].product_id}-->" class="over"><!--商品写真--><img src="<!--{$smarty.const.IMAGE_SAVE_URL|sfTrimURL}-->/<!--{$arrProducts[cnt].main_list_image}-->" alt="<!--{$arrProducts[cnt].name|escape}-->" class="picture" /></a>


のあたりを以下に変更

    <!--▼商品ここから-->
    <div class="listarea">
      <div class="listphoto">
        <!-- ▼DD-Cart -->
        <div class="draggable-product" itemid="<!--{$id}-->">
          <!--★画像★-->
          <a href="<!--{$smarty.const.DETAIL_P_HTML}--><!--{$arrProducts[cnt].product_id}-->" class="over"><!--商品写真--><img src="<!--{$smarty.const.IMAGE_SAVE_URL|sfTrimURL}-->/<!--{$arrProducts[cnt].main_list_image}-->" alt="<!--{$arrProducts[cnt].name|escape}-->" class="picture" /></a>
        </div>
        <!-- ▲DD-Cart -->

ファイルの最後に以下を追加。

<!-- ▼DD-Cart -->
<script type="text/javascript">
$('.draggable-product').draggable({
  containment: 'document',
  helper: 'clone',
  opacity:0.8,
  cursor:'move',
  revert: 'invalid',
  distance: 4,
  start: function(event, ui) {
    // アイテムID
    var id = $(ui.helper).attr('itemid');
    
    // 数量
    var quantity = $('input[name=quantity' + id + ']').val();
    $(ui.helper).attr('quantity', quantity);
    
    // 規格1
    var class1 = $('select[name=classcategory_id' + id + '_1]').val();
    if(class1 != undefined){
        $(ui.helper).attr('class1', class1);
    }
    
    // 規格2
    var class2 = $('select[name=classcategory_id' + id + '_2]').val();
    if(class2 != undefined){
        $(ui.helper).attr('class2', class2);
    }    
  }
});
</script>
<!-- ▲DD-Cart -->

[変更] data/Smarty/templates/default/site_frame.tpl

<script type="text/javascript" src="<!--{$TPL_DIR}-->js/site.js"></script>


の下に以下を追加。

<!-- ▼DD-Cart -->
<script type="text/javascript" src="<!--{$TPL_DIR}-->js/jquery-ui/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="<!--{$TPL_DIR}-->js/jquery-ui/jquery-ui-1.7.2.custom.min.js"></script>
<!-- ▲DD-Cart -->

[変更] data/Smarty/templates/default/site_main.tpl
直後から直前までを以下のdivで囲ってください。

<div id="documentarea">
・・・・
</div>

以上です。試してみてください!