PHP SMARTY 樣板引擎

13. 類別物件

20171129

一、程式 admin_kind.php


<?php
/* 引入檔頭,每支程都會引入 */
require_once 'head.php';
#權限檢查
if(!$_SESSION['isAdmin'])redirect_header("index.php", 3000, "您沒有管理員權限!");

#引入類別物件
include_once WEB_PATH . "/class/ugmKind.php";
 
#取得主要資料庫
$tbl = "ugm_p_kind";

/* 過濾變數,設定預設值 */
$op = system_CleanVars($_REQUEST, 'op', 'opList', 'string');
$sn = system_CleanVars($_REQUEST, 'sn', '', 'int');
$kind = system_CleanVars($_REQUEST, 'kind', 'prod', 'string');
 
#foreign key
$foreign = array(
  "prod" => array("title" => "商品類別","stopLevel"=>1)
);

#實體化 類別物件
#stopLevel
$stopLevel = $foreign[$kind]['stopLevel']; //ugm_tools
$ofsn = 0;
//(資料表,分類,層數,父層)
$ugmKind = new ugmKind($tbl, $kind, $stopLevel, $ofsn);
#---------------------------------
 
#程式流程
switch ($op) {
  //新增資料
  case "opAllInsert":
    $msg = opAllInsert();
    redirect_header($_SESSION['returnUrl'], 3000, $msg);
    exit;
  case "opUpdateSort": //更新排序
    echo opUpdateSort();
  exit;

  case "opSaveDrag": //移動類別儲存
    echo opSaveDrag();
  exit;

  #類別刪除
  case "opDelete" :
    $msg = opDelete($sn);
    redirect_header($_SESSION['returnUrl'], 3000, $msg);
    exit;

  //更新啟用狀態
  case "opUpdateEnable":
    $msg = opUpdateEnable();
    redirect_header($_SESSION['returnUrl'], 3, $msg);
    break;
  #ajax拖曳排序
  case "op_ajax_update_sort":
    echo op_ajax_update_sort();
    exit;
    break;
   
  #新增(Create)
  case "opInsert":
    $msg = opInsert();
    redirect_header($_SESSION['returnUrl'], 3000, $msg);
    exit;

  #更新(Update)
  case "opUpdate":
    $msg = opUpdate($sn);
    redirect_header($_SESSION['returnUrl'], 3000, $msg);
    exit;
    break;
   
  #顯示單筆(Read)
  case "op_show":
    op_show($sn);
    break;
   
  #表單
  case "opForm":
    opForm($sn);
    break;
   
  #讀取(Read)
  default:
    $op = "opList";
    $_SESSION['returnUrl'] = getCurrentUrl();
    opList($kind);
    break;
}

/*---- 將變數送至樣版----*/
$smarty->assign("WEB", $WEB);
$smarty->assign("op", $op);

/*---- 程式結尾-----*/
$smarty->display('theme_admin.tpl');
/*---- 程式結尾-----*/

###########################################################
#  批次編輯資料
###########################################################
function opAllInsert() {
  global $db, $ugmKind;
  foreach ($_POST['title'] as $sn => $title) {
    $title = db_CleanVars($title, "類別名稱");//類別名稱
    $sn = db_CleanVars($sn, "sn");//sn

    $sql = "update `".$ugmKind->get_tbl()."` set
           `title` = '{$title}'
           where sn='{$sn}'";
    $db->query($sql) or redirect_header("", 3000,  $db->error."\n".$sql,true);
  }
  return "更新所有類別完成!";
}
###########################################
#  自動更新排序
###########################################
function opUpdateSort() {
  global $db, $ugmKind;

  $sort = 1;
  foreach ($_POST['tr'] as $sn) {
    if (!$sn) {
      continue;
    }

    $sql = "update `{$ugmKind->get_tbl()}` set 
            `sort`='{$sort}' 
            where `sn`='{$sn}'";

    $db->query($sql) or redirect_header("", 3000,  $db->error."\n".$sql,true);
    $sort++;
  }
  return "排序完成!!";
}
###########################################
#  移動類別儲存
###########################################
function opSaveDrag() {
  global $db,$ugmKind;
  $ofsn = intval($_POST['ofsn']); //目的
  $sn = intval($_POST['sn']); //來源

  $kind = $ugmKind->get_kind(); //關鍵字
  $tbl = $ugmKind->get_tbl(); //資料表
  $stopLevel = $ugmKind->get_stopLevel(); //層次
  //$ofsn = $ugmKind->get_ofsn(); //父層  

  //return $ugmKind->chk_kind_level_down($sn);
   #檢查類別層次
  $thisLevel = $ugmKind->get_thisLevel($sn);
  $downLevel = $ugmKind->get_downLevel($sn);
  $ofsnLevel = $ugmKind->get_thisLevel($ofsn);

  if (!$sn) {
    #根目錄不可移動
    die("根目錄不可移動 (" . date("Y-m-d H:i:s") . ")");
  } elseif ($ofsn == $sn) {
    #自己移至自己
    die("不能自己移至自己(" . date("Y-m-d H:i:s") . ")");
  } elseif ($ofsnLevel + $downLevel >= $stopLevel) {
    #自己往底層移動或自己底下層數+目的所在層數 > 類別層數
    die("子類別太多,請先將子類別移動!(" . date("Y-m-d H:i:s") . ")");
  }

  $sql = "update `{$tbl}`
          set 
          `ofsn`='{$ofsn}' 
          where `sn`='{$sn}'";

  $db->query($sql) or redirect_header("", 3000,  $db->error."\n".$sql,true);
  return "移動類別完成!";
}

###############################
# 刪除類別
###############################
function opDelete($sn){
  global $db,$ugmKind;

  $kind = $ugmKind->get_kind(); //關鍵字
  $tbl = $ugmKind->get_tbl(); //資料表
  $stopLevel = $ugmKind->get_stopLevel(); //層次
  $ofsn = $ugmKind->get_ofsn(); //父層 

  #檢查類別層次
  $downLevel = $ugmKind->get_downLevel($sn);
  if($downLevel)redirect_header($_SESSION['returnUrl'], 3000, "尚有子類別,無法刪除!");

  #刪除商品資料
  $sql = "delete 
          from `{$tbl}`
          where `sn`='{$sn}'
  ";
  $db->query($sql) or redirect_header("", 3000,  $db->error."\n".$sql,true);
 
  return "刪除成功!";
}
###############################################################################
#  更新啟用
###############################################################################
function opUpdateEnable() {
  global $db,$ugmKind;
  #過瀘資料
  $_GET['sn'] = db_CleanVars($_GET['sn'], "sn");
  $_GET['enable'] = db_CleanVars($_GET['enable'], "啟用");
  /****************************************************************/
  //更新
  $sql = "update " . $ugmKind->get_tbl() . " set  
          `enable` = '{$_GET['enable']}' 
          where `sn`='{$_GET['sn']}'";
  $db->query($sql) or redirect_header("", 3000,  $db->error."\n".$sql,true);
  return "更新啟用狀態成功!";
}

###########################################################
#  新增資料
###########################################################
function opInsert() {
  global $db, $ugmKind;
  #驗證token
  verifyToken($_POST['token']);

  $kind = $ugmKind->get_kind(); //關鍵字
  $tbl = $ugmKind->get_tbl(); //資料表
  $stopLevel = $ugmKind->get_stopLevel(); //層次
  $ofsn = $ugmKind->get_ofsn(); //父層

  $_POST['title'] = db_CleanVars($_POST['title'], "類別名稱");//類別名稱
  $_POST['kind'] = db_CleanVars($_POST['kind'], "kind");//分類
  $_POST['enable'] = db_CleanVars($_POST['enable'], "啟用");//狀態
  $_POST['sn'] = db_CleanVars($_POST['sn'], "");//sn
  $_POST['ofsn'] = db_CleanVars($_POST['ofsn'], "ofsn");//ofsn
  //-------------------------------------------------------*/

  #取得排序-----------------------------#
  $sql = "select max(sort) as max_sort
          from `{$tbl}`
          where ofsn='{$_POST['ofsn']}' and kind='{$_POST['kind']}'";

  $result = $db->query($sql) or redirect_header("", 3000,  $db->error."\n".$sql,true);
          
  list($sort) = $row = $result->fetch_row();
  $sort++;

  #---------寫入-------------------------
  $sql = "insert into `{$tbl}`
          (`ofsn` ,`title` , `enable` , `sort`,`kind`)
          values
          ('{$_POST['ofsn']}' , '{$_POST['title']}' ,'{$_POST['enable']}' , '{$sort}' , '{$_POST['kind']}')";
  $db->query($sql) or redirect_header("", 3000,  $db->error."\n".$sql,true);
  //取得最後新增資料的流水編號
  //$_POST['sn'] = $db->insert_id;
  return "新增類別->{$_POST['title']} 成功!";
} 
#################################
# 更新資料
#################################
function opUpdate($sn) {
  global $db,$ugmKind;

  #驗證token
  verifyToken($_POST['token']);

  $kind = $ugmKind->get_kind(); //關鍵字
  $tbl = $ugmKind->get_tbl(); //資料表
  $stopLevel = $ugmKind->get_stopLevel(); //層次
  $ofsn = $ugmKind->get_ofsn(); //父層  
  
  #過濾
  $_POST['title'] = db_CleanVars($_POST['title'], "類別名稱");//類別名稱
  $_POST['kind'] = db_CleanVars($_POST['kind'], "kind");//分類
  $_POST['enable'] = db_CleanVars($_POST['enable'], "啟用");//狀態
  $_POST['sn'] = db_CleanVars($_POST['sn'], "sn");//sn
  $_POST['ofsn'] = db_CleanVars($_POST['ofsn'], "ofsn");//ofsn

  #檢查類別層次
  $thisLevel = $ugmKind->get_thisLevel($sn);
  $downLevel = $ugmKind->get_downLevel($sn);
  $ofsnLevel = $ugmKind->get_thisLevel($_POST['ofsn']);
  if($_POST['ofsn'] == $_POST['sn'])redirect_header($_SESSION['returnUrl'], 3000, "不能設定自己為父類別");
  if($ofsnLevel + $downLevel >= $stopLevel)redirect_header($_SESSION['returnUrl'], 3000, "子類別太多,請先將子類別移動,再更新!");

  $sql = "update `{$tbl}` set
          `ofsn`  = '{$_POST['ofsn']}',
          `title`  = '{$_POST['title']}',
          `kind`  = '{$_POST['kind']}',
          `enable`  = '{$_POST['enable']}'
          where sn='{$_POST['sn']}'";
  $db->query($sql) or redirect_header("", 3000,  $db->error."\n".$sql,true);
  return "編輯類別->{$_POST['title']} 成功!";
}

###############################################################################
#  編輯表單
###############################################################################
function opForm($sn = "") {
  global $db,$smarty,$ugmKind;

  $kind = $ugmKind->get_kind(); //關鍵字
  $tbl = $ugmKind->get_tbl(); //資料表
  $stopLevel = $ugmKind->get_stopLevel(); //層次
  $ofsn = $ugmKind->get_ofsn(); //父層

  //----------------------------------*/
  $_GET['ofsn'] = !isset($_GET['ofsn']) ? 0 : intval($_GET['ofsn']);

  //抓取預設值
  if (!empty($sn)) {
    $row = $ugmKind->get_rowBYsn($sn); 
    $pre = "編輯";
    $row['op'] = "opUpdate";
    //print_r($row);die();
  } else {
    $row = array();
    $pre = "新增";
    $row['op'] = "opInsert";
  }

  $row['formTitle'] = $pre . "類別";
  $row['stopLevel'] = $stopLevel;
  //預設值設定
  //設定「kind_sn」欄位預設值
  $row['sn'] = (!isset($row['sn'])) ? "" : $row['sn'];

  //設定「ofsn」欄位預設值
  $row['ofsn'] = (!isset($row['ofsn'])) ? $_GET['ofsn'] : $row['ofsn'];

  if ($stopLevel > 1) {
    $row['ofsnOption'] = $ugmKind->get_ofsnOption($row['ofsn']);
  }

  //設定「title」欄位預設值
  $row['title'] = (!isset($row['title'])) ? "" : $row['title'];

  //設定「enable」欄位預設值
  $row['enable'] = (!isset($row['enable'])) ? "1" : $row['enable'];

  //設定「kind」欄位預設值
  $row['kind'] = (!isset($row['kind'])) ? $ugmKind->get_kind() : $row['kind'];

  
  $smarty->assign('row', $row);  
  
  #防止偽造表單
  $token = getTokenHTML();
  $smarty->assign("token", $token);
}

#################################
# 列表程式
#################################
function opList($kind) {
  global $db,$smarty,$foreign,$ugmKind;
  # 預設Foreign key=> system
  #---- 防呆
  if (!in_array($kind, array_keys($foreign))) {
    $kind = "prod";
  }
 
  # ----得到foreign key選單 
  $foreignOption = $ugmKind->get_foreignOption($foreign,$kind);
  $foreignForm = "
    <div class='row' style='margin-bottom:10px;'>
      <div class='col-sm-3'>
        <select name='kind' id='kind' class='form-control' onchange=\"location.href='?kind='+this.value\">
          $foreignOption
        </select>
      </div>
    </div>
  ";
  $smarty->assign('foreignForm', $foreignForm);
  $smarty->assign('kind', $kind);
  # ----得到陣列 ----------------------------
  $list = $ugmKind->get_listArr();
  

  $listTitles = array(
    "title" => array("content" => "標題", "width" => 9, "align" => "center"),
    "enable" => array("content" =>"啟用", "width" => 1, "align" => "center"),
    "function" => array("content" => "功能", "width" => 2, "align" => "center"),
  );

  $smarty->assign("listTitles", $listTitles);

  #內容---------------------------------------------------------#
  $listBodys = array(
    "title" => array("valuetype" => "text", "align" => "left"),
    "enable" => array("valuetype" => "yesNo", "align" => "center"),
    "function" => array("valuetype" => "btn", "align" => "center", "btn" => array("edit", "del")), //瀏覽、編輯、刪除
  );

  $listHtml = $ugmKind->get_listHtml($list, $listBodys);
  $smarty->assign("listHtml", $listHtml);
 
  return;
}

二、樣板 admin_kind.tpl


<{if $op=="opList"}>
  <link href="<{$xoAppUrl}>class/treeTable/stylesheets/jquery.treetable.css" rel="stylesheet">
  <link href="<{$xoAppUrl}>class/treeTable/stylesheets/jquery.treetable.theme.default.css" rel="stylesheet">
  <script
  src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
  integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
  crossorigin="anonymous"></script>
  <script type="text/javascript" src="<{$xoAppUrl}>class/treeTable/javascripts/src/jquery.treetable.js"></script>

  <script type="text/javascript">
    $(function()  {
      //可以展開,預設展開
      $('#form_table').treetable({ expandable: true ,initialState: 'expanded' });

      // 配置拖動節點
      $('#form_table .folder').draggable({
        helper: 'clone',
        opacity: .75,
        refreshPositions: true, // Performance?
        revert: 'invalid',
        revertDuration: 300,
        scroll: true
      });

      // Configure droppable rows
      $('#form_table .folder').each(function() {
        $(this).parents('#form_table tr').droppable({
          accept: '.folder',
          drop: function(e, ui) {
            var droppedEl = ui.draggable.parents('tr');
            console.log(droppedEl[0]);
            $('#form_table').treetable('move', droppedEl.data('ttId'), $(this).data('ttId'));
            //alert( droppedEl.data('ttId'));
            //目地的sn :$(this).data('ttId')
            //自己的sn:droppedEl.data('ttId')
            $.ajax({
              type:   'POST',
              url:    '?op=opSaveDrag',
              data:   { ofsn: $(this).data('ttId'), sn: droppedEl.data('ttId'),kind :"<{$kind}>" },
              success: function(theResponse) {
                //$('#save_msg').html(theResponse);

                //警告視窗        
                swal({
                  title: theResponse,
                  text: '自動刷新畫面',
                  type: 'success',
                  showCancelButton: false,
                  confirmButtonColor: '#3085d6',
                  confirmButtonText: '確定'
                }).then(function () {
                  location.href="<{$smarty.session.returnUrl}>";
                });
              }
            });

          },
          hoverClass: 'accept',
          over: function(e, ui) {
            var droppedEl = ui.draggable.parents('tr');
            if(this != droppedEl[0] && !$(this).is('.expanded')) {
              $('#form_table').treetable('expandNode', $(this).data('ttId'));
            }
          }
        });
      });

      //排序
      $('#sort').sortable({ opacity: 0.6, cursor: 'move', update: function() {
          var order = $(this).sortable('serialize') + '&op=opUpdateSort';
          $.post('<{$WEB.file_name}>', order, function(theResponse){
            //$('#save_msg').html(theResponse);//傳回訊息
            
            //警告視窗        
            swal({
              title: theResponse,
              text: '自動刷新畫面',
              type: 'success',
              showCancelButton: false,
              confirmButtonColor: '#3085d6',
              confirmButtonText: '確定'
            }).then(function () {
              location.href="<{$smarty.session.returnUrl}>";
            });
          });
        }
      });

      //每行的删除操作注册脚本事件
      $(".btnDel").bind("click", function(){
        var vbtnDel=$(this);//得到点击的按钮对象
        var vTr=vbtnDel.parents("tr");//得到父tr对象;
        var sn=vTr.attr("sn");//取得 sn
        var title=$("#title_"+sn).val();//取得 title
        //警告視窗        
        swal({
          title: '確定要刪除此資料?',
          text: title,
          type: 'warning',
          showCancelButton: true,
          confirmButtonColor: '#3085d6',
          cancelButtonColor: '#d33',
          confirmButtonText: '確定刪除!',
          cancelButtonText: '取消!'
        }).then(function () {
          location.href="admin_kind.php?op=opDelete&sn=" + sn;
        })

      });

    }); 
  </script>
  <div class="panel panel-primary">
    <div class="panel-heading"><h3 class="panel-title">類別管理</h3></div>

    <div class="panel-body">      
      <div id="save_msg"></div>
      <form action='<{$WEB.file_name}>' method='post' id='myForm'>
        <{$foreignForm}>
        <table id="form_table" class="table table-bordered table-striped table-hover">
          <thead>
            <tr class="active">
              <{foreach from=$listTitles item=row key=k}>
                <th class="col-sm-<{$row.width}> text-<{$row.align}>"><{$row.content}></th>
              <{/foreach}>
            </tr>
          </thead>
          <!-- 根目錄開始 -->
          <tr id='tr_0' data-tt-id="0">
            <td class="text-left" colspan=<{$listTitles|@count}>>
              <span class='folder'></span>
              <i class="fa fa-home" aria-hidden="true"></i>根目錄              

              <a href="#" class="btn btn-success btn-xs" onclick="jQuery('#form_table').treetable('expandAll'); return false;">展開<i class="fa fa-expand" aria-hidden="true"></i></a>

              <a href="#" class="btn btn-danger btn-xs" onclick="jQuery('#form_table').treetable('collapseAll'); return false;">闔起<i class="fa fa-compress" aria-hidden="true"></i></a>

              <a href="#" class="btn  btn-warning btn-xs">重整畫面<i class="fa fa-refresh" aria-hidden="true"></i></a>

              <a href="?op=opForm&kind=<{$kind}>&ofsn=0" class="btn btn-primary btn-xs" ait="在根目錄建立子類別">新增<i class="fa fa-plus" aria-hidden="true"></i></a>
            </td>
          </tr>
          <!-- 根目錄結束 -->

          <tbody id='sort'>
            <{$listHtml}>
          </tbody>

          <tfoot>
            <tr>
              <td colspan=3 class="text-center">
                <input type='hidden' name='op' value="opAllInsert">
                <input type='hidden' name='kind' value="<{$kind}>">
                <button type="submit" class="btn btn-primary">送出</button>
              </td>
            </tr>
          </tfoot>
        </table>
      </form>
    </div>
  </div>
<{/if}>

<{if $op=="opForm"}>
  <div class="panel panel-primary">
    <div class="panel-heading"><h3 class="panel-title"><{$row.formTitle}></h3></div>
    <div class="panel-body">
      <form role="form" action="<{$WEB.file_name}>" method="post" id="myForm" enctype="multipart/form-data">
        <div class="row">
          <div class="col-sm-6">
            <div class="form-group">
              <label>標題</label>
              <input type='text'  name='title' value='<{$row.title}>' id='title' class="form-control">
            </div>
          </div>

          <div class="col-sm-2">
            <div class="form-group">
              <label style="display:block;" >啟用</label>
              <input type='radio' name='enable' id='enable_1' value='1' <{if  $row.enable==1}>checked<{/if}>>
              <label for='enable_1'>啟用</label>&nbsp;&nbsp;
              <input type='radio' name='enable' id='enable_0' value='0' <{if $row.enable==0}>checked<{/if}>>
              <label for='enable_0'>停用</label>
            </div>
          </div>

          <{if $row.stopLevel > 1}>
            <div class="col-sm-2">
              <div class="form-group">
                <label>父類別</label>
                <select name="ofsn" id="ofsn" class="form-control" size="1" >
                  <option value="0">/</option>
                  <{$row.ofsnOption}>
                </select>
              </div>
            </div>
          <{else}>
            <input type='hidden' name='ofsn' value='0'>              
          <{/if}>

        </div>
        <hr>
        <div class="form-group text-center">
          <button type="submit" class="btn btn-primary">送出</button>
          <{if !$row.sn}>
          <button type="reset" class="btn btn-danger">重設</button>
          <{/if}>
          <button type="button" class="btn btn-warning" onclick="location.href='<{$smarty.session.returnUrl}>'">返回</button>
          <input type='hidden' name='op' value='<{$row.op}>'>
          <input type='hidden' name='sn' value='<{$row.sn}>'>
          <input type='hidden' name='kind' value='<{$row.kind}>'>
          <{$token}>
        </div>
      </form>
    </div>
  </div>
<{/if}>

三、使用