1.1 ecshop中最基本的一些知識與操作
①ecshop中的一些公用函數都會放在includes文件夾裏,而這些公用函數幾乎我們都可以用來參照一下就能輕鬆做出我們想要的其他功能了。init.php文件中包含以下代碼,$smarty->assign('lang', $_LANG); 來設置ecshop的語言包。
② ecshop的數據庫操作類是很強大的,一些常用的函數如下:
ECShop的數據操作類文件是includes/cls_mysql.php,類名是cls_mysql。該類主要提供了下面 一些比較有用的方法:
getAll($sql)和getAllCached($sql, $cached = 'FILEFIRST'):獲取所有記錄。
getRow($sql, $limited = false)和getRowCached($sql, $cached = 'FILEFIRST'):獲取單行記錄。
getCol($sqlse)和getColCached($sql, $cached = 'FILEFIRST'):獲取某欄位的所有值。
getOne($sql, $limited = false)和getOneCached($sql, $cached = 'FILEFIRST'):獲取單個數值。
query($sql):執行數據庫查詢。
autoExecute($table, $field_values, $mode = 'INSERT', $where = ''):數據庫表操作。
1.獲取單條記錄
$GLOBALS['db']->getRow($sql);
getRow方法用來從數據庫中獲取滿足條件的單行記錄,或者說是第一條記錄。getRowCached是它的緩存版本,cache key是該方法的第二個參數,如果緩存有效,直接返回緩存結果,否則重新執行數據庫查詢。緩存功能依此類推。
例如查詢數據庫表中的某個字段
$sql = "SELECT level FROM " . $ecs->table('users') . " WHERE user_name = '$vipaccount' ";
$row = $GLOBALS['db']->getRow($sql);
$level = $row['level'];
2.獲取單一字段
$GLOBALS['db']->getOne($sql);
例如查詢產品總數:
echo $GLOBALS['db']->getOne(‘SELECT COUNT(*) FROM ‘ . $GLOBALS['ecs']->table(‘goods’) ;
查詢單一數據:
$sql = "SELECT order_amount FROM " . $GLOBALS['ecs']->table('order_info') . " WHERE order_id = '$order_id'";
$order_amount = $GLOBALS['db']->getOne($sql);
3.獲取所有記錄
$sql = 'SELECT * FROM ' . $GLOBALS['ecs']->table('suppliers') . ' ORDER BY suppliers_name ASC';
$GLOBALS['db']->getAll($sql);
4.獲取單行記錄
getCol方法用來從數據庫中獲取滿足條件的某個欄位的所有值
$sql = "SELECT pay_id FROM " . $GLOBALS['ecs']->table('payment');
$GLOBALS['db']->getCol($sql);
getCol()得到是一個一維數組,而 getAll()得到的經常是一個二維數組。
5.執行sql語句
$GLOBALS['db']->query($sql); //執行刪除(DELETE),插入(INSERT),更新(UPDATE)等操作可用此方法
6.把數組元素插入數據庫
$parent['goods_number'] = ’1′;
$parent['parent_id'] = 0;
$GLOBALS['db']->autoExecute($GLOBALS['ecs']->table(‘cart’), $parent, ‘INSERT’);//table裏面是要插入的表,第二個參數是數組
7.更新數據(數組形式)
$user = array(“username” => “ego”);
$GLOBALS['db']->autoExecute($GLOBALS['ecs']->table('users'), $user, 'UPDATE', "user_id = '$user_id'");
執行的SQL: UPDATE ecs_users SET username = ‘ego’ WHERE user_id = $user_id 。如果$user 中有兩個元素,則 sql中的set 也寫兩個。
③echsop中的模板
ecshop有強大的模版機制,ECSHOP 結合Dreamweaver實現了一套模版機制,改動模版不再需要上傳,而是在後臺稍稍動動手設置一下就可以了。
ecshop採用smarty模板技術,Smarty是一個使用PHP寫出來的模板引擎,是目前業界最著名的PHP模板引擎之一。它分離了邏輯代碼和外在的內容,提供了一種易於管理和使用的方法,用來將原本與HTML代碼混雜在一起PHP代碼邏輯分離。
在php文件中使用assign方法把php變量傳值給模板文件,例如:
$smarty->assign(‘action’, $action); //把php中的變量$action傳值給模板文件(*.dwt)中的’action’變量,模板文件可用{$action}來輸出此值。
使用display方法來指定當前的模板文件並輸出到該文件顯示到客戶端,例如:$smarty->display(‘user_passport.dwt’);
1.2 補習一下前端的知識
1.輸入框內容只可讀。
2.在一張背景圖片上顯示輸入框及文字。eg: 做一個登陸頁面,在一個圖片上書寫用戶名 ,密碼,登陸按鈕等。
3.如何獲取下拉框中的value值。
<</span>body> <</span>form onsubmit="return formValidate()"> <</span>select name="select"id="order_status" onchange="a()"> <</span>option value="11">--請選擇訂單狀態--</</span>option><</span>option value="0">--未確認--</</span>option> <</span>option value="1">--已確認--</</span>option> </</span>select> <</span>select name="select" id="shipping_status"onchange="b()"> <</span>option value="22">--請選擇商品配送情況--</</span>option> <</span>optionvalue="0">--未發貨--</</span>option> <</span>option value="1">--已發貨--</</span>option><</span>option value="2">--已收貨--</</span>option> </</span>select> <</span>input type="submit"value="提交" /> </</span>form> </</span>body> </</span>html>
javascript方法如下
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <</span>htmlxmlns="http://www.w3.org/1999/xhtml"> <</span>head> <</span>meta http-equiv="Content-Type"content="text/html; charset=gb2312" /> <</span>title>無標題文檔</</span>title> <</span>scriptlanguage="JavaScript"> function formValidate() { var t1 =document.getElementByIdx_x("order_status"); alert(t1.options[t1.selectedIndex].value); var t2 =document.getElementByIdx_x("shipping_status"); alert(t2.options[t2.selectedIndex].value); returnfalse; } </</span>script> </</span>head>
這樣點擊提交按鈕之後,便可以將value值得到並alert出選擇的值。如果要將得到的 order_status 和 shipping_staus 以隱藏地方式寫在頁面上,則可以在formValidate()方法里加上如下幾句
$firstparam = t1.options[t1.selectedIndex].value; $secondparam = t2.options[t2.selectedIndex].value; document.getElementByIdx_x("firstparam").value =$firstparam ; document.getElementByIdx_x("secondparam").value=$secondparam ;
頁面上有如下代碼,就可以把下拉框中的值賦給firstparam和secondparam。
<</span>tr> <</span>td><</span>input id="firstparam" name="firstparam" type="hidden" value="" /></</span>td> <</span>td><</span>input id="secondparam" name="secondparam" type="hidden" value=""/></</span>td> </</span>tr>
最後將頁面的值傳遞給php代碼中。這樣寫可以得到這兩個參數。
$order_status = $_POST['firstparam']; $shipping_status = $_POST['secondparam'];
到此,整個過程完成:用戶選擇頁面下拉框中的值後可以傳遞給php代碼用來進行下一步的操作,比如寫sql語句。
4.檢查表單輸入的密碼是否相同的js函數
1.3 如何在ecshop後臺增加左側導航欄
1.後臺添加導航
在languages\zh_cn\admin\common.php中增加
$_LANG['99_meicheng'] = ‘批量管理’; $_LANG['01_meicheng_user'] = ‘批量添加用戶’;
2.添加導航鏈接
在 admin\includes\inc_menu.php中增加
$modules['99_meicheng']['01_meicheng_user'] = ‘mc_user.php’;
其中 mc_user.php就是在admin文件夾中控制此次操作的php文件。
3.後續要添加的有 financial_export.htm,此文件用來展示。在admin\templates中。還有一個是語言文件,名稱與admin中的php文件名稱相同。在languages\zh_cn\admin中。
1.4 ecshop中讀取csv文件內容
1.其實這個可以稱的上是php中讀取csv文件內容。例子是讀取csv中的用戶信息,後臺註冊用戶。
用戶操作界面代碼:
<</span>div class="main-div"> <</span>form action="ck_user.php" method="post"enctype="multipart/form-data" name="theForm" > <</span>table width="100%" id="general-table"><</span>tbody id="1" > <</span>tr> <</span>td class="label">批量導入用戶:</</span>td> <</span>td><</span>input name="upfile" type="file" id="upfile" size='35' /> <</span>spanstyle="color:#F00;">*</</span>span>文本爲csv格式的:例 user.csv </</span>td> </</span>tr></</span>tbody> <</span>tr> <</span>td class="label"> </</span>td> <</span>td> <</span>inputtype="submit" value="批量添加用戶" class="button" /> <</span>input type="hidden" name="act"value="mc_add" /> </</span>td> </</span>tr> </</span>table> </</span>form> </</span>div>
其中 enctype="multipart/form-data" 這一點要注意,因爲提交的數量是cvs文件。
後臺讀取cvs文件以及插入數據庫中的代碼:內容較多,請穩步這裏 http://www.cnblogs.com/chenkaiadd/archive/2013/05/25/3099027.html
1.5 ecshop中將數據庫表中的數據導出成csv文件
1.6 ecshop 中前端分頁 (包含上架時間、價格、更新時間這一欄的鏈接、還有頁中前一頁、後一頁、輸入數字後點擊確定跳轉到指定頁)
1.7 ecshop 中後臺分頁
1.8 ecshop 中ajax使用
①首先ecshop是如何定義ajax對象的。
ecshop中的ajax對象是在js/transport.js文件中定義的。裏面是ajax對象文件。聲明瞭一個var Ajax = Transport;對象和一個方法Ajax.call = Transport.run;
②ecshop中ajax可以使用兩種方式傳遞數據.一種是get方式,一種是post方式.
Ajax.call( 'user.php?act=is_registered', 'username=' + username, registed_callback , 'GET', 'TEXT', true, true ); Ajax.call('user.php?act=return_to_cart', 'order_id=' + orderId, returnToCartResponse, 'POST', 'JSON');
③ecshop中的 ajax可以是傳遞text數據,也可以是一個json對象。比如以下代碼 裏面的goods就是對象.而且是靠json來傳遞的。返回的結果result也是對象.
goods.quick = quick; goods.spec = spec_arr; goods.goods_id = goodsId; goods.number = number; goods.parent = (typeof(parentId) == "undefined") ? 0 : parseInt(parentId); Ajax.call('flow.php?step=add_to_cart', 'goods=' + goods.toJSONString(), addToCartResponse, 'POST', 'JSON');
④ecshop ajax函數裏面.第三個參數就是回掉函數的名稱。比如以上代碼addToCartResponse 這個函數就是ajax處理結果的回調函數.
⑤在ecshop的php代碼中,一般是通過get或者post方式來接受函數。比如以下例子,如果接受的是對象。還需要用json數據格式來處理.比如以下
include_once('includes/cls_json.php'); $_POST['goods'] = json_str_iconv($_POST['goods']); 處理的返回結果,也需要是json格式發送給js die($json->encode($result));
下面以ecshop中的用戶註冊中驗證用戶名是否存在來分析。
在user_passport.dwt頁面中顯示註冊界面
<</span>form action="user.php" method="post" name="formUser" onsubmit="return register();"><</span>table width="100%" border="0" align="left" cellpadding="5" cellspacing="3"> <</span>tr><</span>td width="13%" align="right">{$lang.label_username}</</span>td> <</span>td width="87%"><</span>input name="username" type="text" size="25" id="username"onblur="is_registered(this.value);" class="inputBg"/> <</span>span id="username_notice"style="color:#FF0000"> *</</span>span> </</span>td> </</span>tr> <</span>tr> <</span>td> </</span>td> <</span>td align="left"> <</span>input name="act" type="hidden"value="act_register" > <</span>input type="hidden" name="back_act" value="{$back_act}" /> <</span>input name="Submit" type="submit" value="" class="us_Submit_reg"> </</span>td> </</span>tr> </</span>table> </</span>form>
在頁面中有 onblur 這個方法,鼠標離開後調用這個方法。然後在user.js中會有is_registered(username)這個方法的實現
function is_registered( username ) { var submit_disabled = false; var unlen = username.replace(/[^\x00-\xff]/g, "**").length; if ( username == '' ) { document.getElementByIdx_x('username_notice').innerHTML = msg_un_blank; var submit_disabled =true; } if ( !chkstr( username ) ) { document.getElementByIdx_x('username_notice').innerHTML =msg_un_format; var submit_disabled = true; } if ( unlen < 3 ) { document.getElementByIdx_x('username_notice').innerHTML = username_shorter; var submit_disabled =true; } if ( unlen > 14 ) { document.getElementByIdx_x('username_notice').innerHTML =msg_un_length; var submit_disabled = true; } if ( submit_disabled ) { document.forms['formUser'].elements['Submit'].disabled = 'disabled'; return false; } Ajax.call('user.php?act=is_registered', 'username=' + username, registed_callback , 'GET', 'TEXT', true,true ); }
該函數調用 ajax方法。
Ajax.call( 'user.php?act=is_registered', 'username=' + username, registed_callback , 'GET', 'TEXT', true, true ); ajax方法調用 user.php中的 elseif ($action == 'is_registered') 於是user.php頁面中有如下代碼,主要是調用了check_user 這個方法去數據庫查找是否用戶名已經註冊。
elseif ($action == 'is_registered') { include_once(ROOT_PATH . 'includes/lib_passport.php'); $username = trim($_GET['username']); $username = json_str_iconv($username); //將JSON傳遞的參數轉碼 if ($user->check_user($username) || admin_registered($username)) { echo 'false'; } else { echo 'true'; } }
check_user方法調用的是 \includes\modules\integrates\ecshop.php裏的,不是父類 integrate.php 裏面的方法。檢查指定用戶是否存在及密碼是否正確(重載基類check_user函數,支持zc加密方法).
ajax方法中的第三個參數是一個回調函數的名稱,該函數的實現也是在 user.js中。他的參數result正是上面函數echo的值(具體原因我暫時還不明白)
function registed_callback(result) { if ( result == "true" ) { document.getElementByIdx_x('username_notice').innerHTML = msg_can_rg; document.forms['formUser'].elements['Submit'].disabled = ''; } else { document.getElementByIdx_x('username_notice').innerHTML = msg_un_registered; document.forms['formUser'].elements['Submit'].disabled = 'disabled'; } }
該方法通過返回的結果 來判斷在提示信息的文本框中到底顯示什麼內容。
在這裏看看ajax的介紹,總結的不錯。
這幾天出現了一個問題,就是用自帶的ajax.call函數不好用,於是想自己寫一個,雖然最後證明自己寫完了問題依然存在,可能不是ajax的方法,但是過程還是記錄下來。
前臺頁面代碼
<</span>input type="text" onblur=" is_specialed(this.value);
js 裏方法如下
function is_specialed( service_special ) { if ( service_special == '向店內工作人員獲取服務專員號'|| service_special == '') { document.getElementByIdx_x('special_error').innerHTML = "該服務專員號不存在2"; document.getElementByIdx_x('special_error').className = "error"; document.forms['formRegist'].elements['ifCanReg'].value = "1"; } // Ajax.call( 'user.php?act=is_specialed', 'service_special=' + service_special, specialed_callback , 'GET', 'TEXT', true, true ); //加上 自己寫的ajax //調用ajaxstart xmlHttp=GetXmlHttpObject(); if (xmlHttp==null) { alert ("您的瀏覽器不支持AJAX!"); return; } var url="user.php?act=is_specialed"; url=url+"&sid="+Math.random() + "&service_special="+ service_special;xmlHttp.onreadystatechange=function () { //請求狀態是4的時候,請求處理完成 if(xmlHttp.readyState==4) { //response是得到的後臺輸出數據。 var response = xmlHttp.responseText ;//這裏將用回調函數用來顯示錯誤提示信息 specialed_callback(response); } } xmlHttp.open("get",url,true); xmlHttp.send(); //調用ajax end } //創建xmlhttprequest對象 functionGetXmlHttpObject() { var xmlHttp=null; try { // Firefox, Opera 8.0+, Safari xmlHttp=newXMLHttpRequest(); } catch (e) { // Internet Explorer try { xmlHttp=newActiveXObject("Msxml2.XMLHTTP"); } catch (e) { xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); } }return xmlHttp; } function specialed_callback(result) { result = result.replace(/\s/g,""); if ( result == 'true' ) { //document.forms['formRegist'].elements['registsubmit'].disabled = '';document.forms['formRegist'].elements['ifCanReg'].value = "0"; } else { document.getElementByIdx_x('special_error').innerHTML = "該服務專員號不存在"; document.getElementByIdx_x('special_error').className = "error";//document.forms['formRegist'].elements['registsubmit'].disabled = 'disabled';document.forms['formRegist'].elements['ifCanReg'].value = "1"; } }
ajax裏面使用的是get方法提交的。後臺user.js裏面可以通過$_GET['service_special'] 和$REQUEST['service_special']來得到。如果用ajax用post方法提交,則可以這樣寫
xmlHttp.open("POST",url,true); xmlHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");//如果需要像HTML表單那樣POST 數據用 setRequestHeader() 來添加 HTTP 頭xmlHttp.send("service_special="+service_special);
相應的user.php 利用 $_POST['service_special'] 或$_REQUEST['service_special']來得到數據。代碼爲
elseif ($action == 'is_specialed') { include_once(ROOT_PATH . 'includes/lib_passport.php'); //$service_special = trim($_GET['service_special']); $service_special = trim($_POST['service_special']); //$service_special = trim($_REQUEST['service_special']); //$username = json_str_iconv($username); $sqlspecial = 'SELECT level FROM ' . $GLOBALS['ecs']->table('users') . " WHERE user_name = '$service_special'"; $row =$GLOBALS['db']->getRow($sqlspecial); $level = $row['level']; if($level ==1){ echo 'true'; } else { echo 'false'; } }
關於用get還是post 官方的說法是
與 POST 相比,GET 更簡單也更快,並且在大部分情況下都能用。
然而,在以下情況中,請使用 POST 請求:
- 無法使用緩存文件(更新服務器上的文件或數據庫)
- 向服務器發送大量數據(POST 沒有數據量限制)
- 發送包含未知字符的用戶輸入時,POST 比 GET 更穩定也更可靠
1.9 ecshop中的後臺權限管理
①以
admin\group_buy.php爲例來說明權限的運用 。首先說一下,ecshop中每個php文件的頭兩行都是引用這樣兩句話,算是初始化吧。
define('IN_ECS', true); require(dirname(__FILE__) . '/includes/init.php');
隨後我們看到,後臺每個文件在執行方法之前都會有類似這樣一個權限檢查的方法
admin_priv('group_by');
②該方法的實現在admin\includes\lib_main.php中,具體如下圖。
function admin_priv($priv_str, $msg_type = '' , $msg_output = true) { global $_LANG; if($_SESSION['action_list'] == 'all') { return true; } if (strpos(',' . $_SESSION['action_list'] . ',', ',' . $priv_str . ',') === false) { $link[] = array('text' => $_LANG['go_back'], 'href' => 'javascript:history.back(-1)'); if ( $msg_output) { sys_msg($_LANG['priv_error'], 0, $link); }return false; } else { return true; } }
從這裏我們可以看出,這裏運用了php中的 strpos函數。該函數作用爲: 返回字符串在另一個字符串中第一次出現的位置。如果沒有找到該字符串,則返回 false。
語法爲:strpos(string,find,start) 其中,string 爲必需,是被搜索的字符串,find爲必需,是要查找的字符。start爲可選,是開始搜索的位置。另外一個知識點:如果需要對大小寫不敏感的操作,則可以用函數 stripos() 來解決。
還有一個知識點是php中的 ==是等值,是不判斷二者是否是同一數據類型,而 ===是等價它不但要求二者值相等,而且還要求它們的數據類型也相同。
從類的註釋中我們已經很清楚。該函數的功能就是查看 ,'group_by', 這個字符串在不在當前用戶的session 中 ,如果不在,則提示用戶 對不起,您沒有執行此項操作的權限! 並將函數結果返回false,此時程序結束。不能進行到下一步。需要說明的是 admin這個用戶,默認的權限爲 'all' ,這一點可以在數據庫中的表admin_user中的 action_list 這一字段中對應的值看出。 其它管理員的權限則是由 admin在後臺管理的【管理員列表】這一菜單欄下分配的權限,效果如圖,一旦打上勾,則具有了該權限,也即admin_user表中的 action_list 字段值就增加一個內容。
③那麼系統是如何將數據庫中的這些action_list 裏的值存放到管理員的session中的呢?
控制後臺登陸是在admin\privilege.php裏 elseif ($_REQUEST['act'] == 'signin') 方法裏有這樣一段代碼。
$_POST['username'] = isset($_POST['username']) ? trim($_POST['username']) : '';$_POST['password'] = isset($_POST['password']) ? trim($_POST['password']) : ''; $sql = "SELECT user_id, user_name, password, last_login, action_list, last_login". " FROM " . $ecs->table('admin_user') . " WHERE user_name = '" . $_POST['username']. "' AND password = '" .md5($_POST['password']) . "'"; $row = $db->getRow($sql); if ($row) { // 登錄成功set_admin_session($row['user_id'], $row['user_name'], $row['action_list'], $row['last_login']);
.......
可以看到管理員登陸時,利用 set_admin_session 這個方法將數據庫中的action_list中的字段設置到SESSION中。
在admin\includes\lib_main.php裏有這個方法的實現。
function set_admin_session($user_id, $username, $action_list, $last_time) { $_SESSION['admin_id'] = $user_id; $_SESSION['admin_name'] = $username; $_SESSION['action_list'] = $action_list;$_SESSION['last_check'] = $last_time; // 用於保存最後一次檢查訂單的時間 }
至此,可以看到整個流程已經非常清楚。管理員在數據庫表中有一個字段 action_list, 管理員登陸之後將這個數據設置到$_SESSION 中。以後每次操作時都會調用admin_priv() 這個函數。來檢驗當前用戶是否具有這個權限,如果沒有則返回false ,程序終止。
④至於上圖中點擊勾後,便具有該權限。是如何實現的呢,權限等級,子權限等是如何實現的呢?參見:
⑤ ecshop中的_SESSION工作原理。
在ec_shop中有兩個表(ecs_sessions和ecs_sessions_data),這兩個表用於記錄、管理session,默認情況下使用的是myISAM引擎。嘗試將這兩個表改成memory引擎,提高session讀取效率。
1、將兩個表複製,做好備份:
CREATE TABLE ecs_sessions_ori SELECT * FROM ecs_sessions;
CREATE TABLE ecs_sessions_data_ori SELECT * FROM ecs_sessions_data;
2、將原有的兩張表更改成內存表:
ALTER TABLE `ecs_sessions` ENGINE = MEMORY
3、ecs_sessions_data表不能更改成內存表:
因爲其中含有text字段,因而這個表不能被作爲內存表使用。暫時先不改這個表了。 參考如下Memory引擎的使用場景及特點:
能像會話(Session)或緩存(Caching)一樣方便操作和管理。
充分發揮內存引擎的特點:高速度,低延遲。
只讀或讀爲主的訪問模式(不適合頻繁寫)。
但是內存表的性能受制於單線程的執行效率和寫操作時的表鎖開銷,這就限制了內存表高負載時的擴展性,特別是混合寫操作的併發處理。此外,內存表中的數據在服務器重啓後會丟失。
從這裏我們可以暫時推斷出,可能ecshop中要建這兩個表的目的是爲了提高效率。這兩個表有一個不同點就是 `data` char(255) NOT NULL COMMENT '序列化後的session數據,如果session數據大於255則將數據存到表ecs_sessions_data,此處爲空', 而ecs_sessions_data 裏的 `data` longtext NOT NULL COMMENT 'session序列化後的數據'。
這裏又有另一個知識點: ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='session數據表(超過255字節的session內容會保存在該表)'; engine 的意義:參見另一篇:http://www.cnblogs.com/chenkaiadd/archive/2013/04/21/3033964.html
下面開始講解 ecshop中的session的原理。
PHP 中的許多預定義變量都是“超全局的”,這意味着它們在一個腳本的全部作用域中都可用。在函數或方法中無需執行 global $variable; 來訪問它們。
$GLOBALS $_SERVER $_GET $_POST $_FILES $_COOKIE $_SESSION $_REQUEST $_ENV 這其中就有$_SESSION
1.9 ecshop中商品篩選下的品牌顯示(左連接的SQL語句的寫法)
①先看效果圖,即點擊某一產品分類後,將數據庫中的這一類產品的品牌展示出來。