从11月6号入职到现在,接触ecshop,接触php,确实让我收获了一些东西。总是想写一下总结,一直没下笔。这几天总结一下,也算是对前几个月的工作的一个回顾。
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’);
④echsop中的常用问题集合
这个网址 http://lq8034.blogbus.com/logs/187172857.html
⑤在e
1.2 补习一下前端的知识
1.输入框内容只可读。
2.在一张背景图片上显示输入框及文字。eg: 做一个登陆页面,在一个图片上书写用户名 ,密码,登陆按钮等。
3.如何获取下拉框中的value值。
<body> <form onsubmit="return formValidate()"> <select name="select" id="order_status" onchange="a()"> <option value="11">--请选择订单状态--option> <option value="0">--未确认--option> <option value="1">--已确认--option> select> <select name="select" id="shipping_status" onchange="b()"> <option value="22">--请选择商品配送情况--option> <option value="0">--未发货--option> <option value="1">--已发货--option> <option value="2">--已收货--option> select> <input type="submit" value="提交" /> form> body> html>
javascript方法如下
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>无标题文档title>
<script language="JavaScript">
function formValidate()
{
var t1 = document.getElementById("order_status");
alert(t1.options[t1.selectedIndex].value);
var t2 = document.getElementById("shipping_status");
alert(t2.options[t2.selectedIndex].value);
return false;
}
script>
head>
这样点击提交按钮之后,便可以将value值得到并alert出选择的值。如果要将得到的 order_status 和 shipping_staus 以隐藏地方式写在页面上,则可以在formValidate()方法里加上如下几句
$firstparam = t1.options[t1.selectedIndex].value;
$secondparam = t2.options[t2.selectedIndex].value;
document.getElementById("firstparam").value =$firstparam ;
document.getElementById("secondparam").value=$secondparam ;
页面上有如下代码,就可以把下拉框中的值赋给firstparam和secondparam。
<tr> <td><input id="firstparam" name="firstparam" type="hidden" value="" />td> <td><input id="secondparam" name="secondparam" type="hidden" value="" />td> 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中的用户信息,后台注册用户。
用户操作界面代码:
<div class="main-div"> <form action="ck_user.php" method="post" enctype="multipart/form-data" name="theForm" > <table width="100%" id="general-table"> <tbody id="1" > <tr> <td class="label">批量导入用户:td> <td> <input name="upfile" type="file" id="upfile" size='35' /> <span style="color:#F00;">*span>文本为csv格式的:例 user.csv td> tr> tbody> <tr> <td class="label"> td> <td> <input type="submit" value="批量添加用户" class="button" /> <input type="hidden" name="act" value="mc_add" /> td> tr> table> form> 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页面中显示注册界面
<form action="user.php" method="post" name="formUser" onsubmit="return register();"> <table width="100%" border="0" align="left" cellpadding="5" cellspacing="3"> <tr> <td width="13%" align="right">{$lang.label_username}td> <td width="87%"> <input name="username" type="text" size="25" id="username" onblur="is_registered(this.value);" class="inputBg"/> <span id="username_notice" style="color:#FF0000"> *span> td> tr> <tr> <td> td> <td align="left"> <input name="act" type="hidden" value="act_register" > <input type="hidden" name="back_act" value="{$back_act}" /> <input name="Submit" type="submit" value="" class="us_Submit_reg"> td> tr> table> 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.getElementById('username_notice').innerHTML = msg_un_blank; var submit_disabled = true; } if ( !chkstr( username ) ) { document.getElementById('username_notice').innerHTML = msg_un_format; var submit_disabled = true; } if ( unlen < 3 ) { document.getElementById('username_notice').innerHTML = username_shorter; var submit_disabled = true; } if ( unlen > 14 ) { document.getElementById('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.getElementById('username_notice').innerHTML = msg_can_rg; document.forms['formUser'].elements['Submit'].disabled = ''; } else { document.getElementById('username_notice').innerHTML = msg_un_registered; document.forms['formUser'].elements['Submit'].disabled = 'disabled'; } }
该方法通过返回的结果 来判断在提示信息的文本框中到底显示什么内容。
在这里看看ajax的介绍,总结的不错。
这几天出现了一个问题,就是用自带的ajax.call函数不好用,于是想自己写一个,虽然最后证明自己写完了问题依然存在,可能不是ajax的方法,但是过程还是记录下来。
前台页面代码
<input type="text" onblur=" is_specialed(this.value);
js 里方法如下
/* chenkai 20130702 检查服务专员号是否存在*/ function is_specialed( service_special ) { if ( service_special == '向店内工作人员获取服务专员号'|| service_special == '') { document.getElementById('special_error').innerHTML = "该服务专员号不存在2"; document.getElementById('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对象 function GetXmlHttpObject() { var xmlHttp=null; try { // Firefox, Opera 8.0+, Safari xmlHttp=new XMLHttpRequest(); } catch (e) { // Internet Explorer try { xmlHttp=new ActiveXObject("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.getElementById('special_error').innerHTML = "该服务专员号不存在"; document.getElementById('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中,具体如下图。
/** * 判断管理员对某一个操作是否有权限。 * * 根据当前对应的action_code,然后再和用户session里面的action_list做匹配,以此来决定是否可以继续执行。 * @param string $priv_str 操作对应的priv_str * @param string $msg_type 返回的类型 * @return true/false */ 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里有这个方法的实现。
/** * 设置管理员的session内容 * * @access public * @param integer $user_id 管理员编号 * @param string $username 管理员姓名 * @param string $action_list 权限列表 * @param string $last_time 最后登录时间 * @return void */ 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语句的写法)
①先看效果图,即点击某一产品分类后,将数据库中的这一类产品的品牌展示出来。
②我做的是将数据库中的某一类被标记成'团购'的产品展示出来,并将品牌也展示,然后点击品牌,展示的产品跟着被筛选出来。
在group_buy_list.dwt负责显示的页面里有
<dl class='fore'> <dt>品牌:dt> <dd> <div><a href="{$brand.url}" class="curr">{$brand.brand_name}a>div> <div><a href="{$brand.url}">{$brand.brand_name}a>div> dd> dl>
其中$brands是在group_buy.php里这样赋值的
/* 利用这个sql 语句将正在进行的渠道活动的所有品牌全部展示出来*/ $sql = "SELECT b.brand_id, b.brand_name, COUNT(*) AS goods_num ". "FROM " . $GLOBALS['ecs']->table('brand') . "AS b ". "LEFT JOIN " . $GLOBALS['ecs']->table('goods') . " AS g ON b.brand_id = g.brand_id " . " LEFT JOIN ". $GLOBALS['ecs']->table('goods_activity') . " AS gc ON g.goods_id = gc.goods_id ". "WHERE gc.act_type = '" . GAT_GROUP_BUY . "' " . "AND gc.is_finished < 3 AND gc.checked =1 " . "GROUP BY b.brand_id HAVING goods_num > 0 ORDER BY b.sort_order, b.brand_id ASC"; $brands = $GLOBALS['db']->getAll($sql);
效果如图所示
如果将最后一句换成如下所示,结果一样。
"GROUP BY b.brand_id ORDER BY b.sort_order, b.brand_id ASC";
如果将最后一句的group by 去掉 结果为
" ORDER BY b.sort_order, b.brand_id ASC";
如果把 count(*) As goods_num 去掉 即sql语句这样写,会出现如下结果
$sql = "SELECT b.brand_id, b.brand_name ". "FROM " . $GLOBALS['ecs']->table('brand') . "AS b ". "LEFT JOIN " . $GLOBALS['ecs']->table('goods') . " AS g ON b.brand_id = g.brand_id " . " LEFT JOIN ". $GLOBALS['ecs']->table('goods_activity') . " AS gc ON g.goods_id = gc.goods_id ". "WHERE gc.act_type = '" . GAT_GROUP_BUY . "' " . "AND gc.is_finished < 3 AND gc.checked =1 " ; "GROUP BY b.brand_id ORDER BY b.sort_order, b.brand_id ASC";
③重新来看这个问题:要将参加了'团购活动的产品的品牌'显示出来。查找品牌表'brand' 左连商品表'goods' 关系是 b.brand_id = g.brand_id 。然后是参加了团购活动的商品的筛选。商品活动表'goods_activity'这个表中有记录。继续左连,关系为 g.goods_id = gc.goods_id。
根据以上的分析,可以写出这样的SQL语句,但是效果和上图一样。
$sql = "SELECT b.brand_id, b.brand_name ". "FROM " . $GLOBALS['ecs']->table('brand') . "AS b ". "LEFT JOIN " . $GLOBALS['ecs']->table('goods') . " AS g ON b.brand_id = g.brand_id " . " LEFT JOIN ". $GLOBALS['ecs']->table('goods_activity') . " AS gc ON g.goods_id = gc.goods_id ". "WHERE gc.act_type = '" . GAT_GROUP_BUY . "' " . "AND gc.is_finished < 3 AND gc.checked =1 " . " ORDER BY b.sort_order, b.brand_id ASC";
由结果可以看出,数据重复了,只要在ORDER BY 前面加上 一句 group by b.brand_id 就可以解决问题了。
另外值得说的一点是排序的问题,两个条件可以用逗号隔开排序。默认是asc 升序。如果在降序,可以用desc 。至于前面的 COUNT(*) AS goods_num 以及后面的 having goods_num > 0 其实可以不用写的。
2.1 php中的数组
3.1 ecshop中的一些正则判断(lib_common.php里)
①验证输入的邮件地址是否合法,代码如下:
function is_email($user_email) { $chars = "/^([a-z0-9+_]|\\-|\\.)+@(([a-z0-9_]|\\-)+\\.)+[a-z]{2,6}\$/i"; if (strpos($user_email, '@') !== false && strpos($user_email, '.') !== false) { if (preg_match($chars, $user_email)) { return true; } else { return false; } } else { return false; } }
分析:
preg_match()返回pattern的匹配次数. 它的值将是0次(不匹配)或1次, 因为preg_match()在第一次匹配后 将会停止搜索.preg_match_all()不同于此, 它会一直搜索subject直到到达结尾. 如果发生错误preg_match()返回FALSE.
②检查是否为手机号,代码如下:
function is_telephone($phone){ $chars = "/^13[0-9]{1}[0-9]{8}$|15[0-9]{1}[0-9]{8}$|18[0-9]{1}[0-9]{8}$/"; if (preg_match($chars, $phone)){ return true; } }
分析: php中还提供 filter_var() 函数通过指定的过滤器过滤变量。 如果成功,则返回已过滤的数据,如果失败,则返回 false。
php if(!filter_var("[email protected]", FILTER_VALIDATE_EMAIL)) { echo("E-mail is not valid"); } else { echo("E-mail is valid"); } ?>
输出结果为 E-mail is not valid
3.2 ecshop中的获取时间
刚才有个需求是批量注册时把注册用户的时间记录下来。ecshop在includes\lib_time.php中有个方法 gmtime,可以获得当前格林威治时间的时间戳 代码就一句话。
/** * 获得当前格林威治时间的时间戳 * * @return integer */ function gmtime() { return (time() - date('Z')); }
然后在sql语句中是这样引用的。最后一个参数。
$sql = "insert into ".$GLOBALS['ecs']->table('users'). " set user_name='".$info['user_name']."' , password ='".md5($info['password'])."' , email ='".$info['email']."' , sex = '".$info['sex']."' , user_money ='".$info['user_money']."' , frozen_money ='".$info['frozen_money']."' , birthday = '".$info['birthday ']."' , mobile_phone = '".$info['mobile_phone']."' , higherlevel_name = '".$info['higherlevel_name']."' , salesname = '".$info['salesname']."' , level = '".$info['level']."' , usertype = '".$info['usertype']."' , typediscount = '".$info['typediscount']."' , idnumber = '".$info['idnumber']."' , reg_time = '".gmtime()."' ";