欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  php教程

【PHP开源产品】Ecshop的商品筛选功能实现分析之一

程序员文章站 2022-04-27 08:08:13
...

一、首先,说明一下为什么要对category.php文件进行分析。 原因如下: ①个人对商城类商品筛选功能的实现比较好奇; ②对商城中关于商品的数据表设计比较感兴趣。(该功能涉及到与数据库的交互,而且与数据库中数据表的设计好坏有一定的联系); ③多条件(

一、首先,说明一下为什么要对category.php文件进行分析。

原因如下:

①个人对商城类商品筛选功能的实现比较好奇;

②对商城中关于商品的数据表设计比较感兴趣。(该功能涉及到与数据库的交互,而且与数据库中数据表的设计好坏有一定的联系);

③多条件(属性)筛选功能在现今的很多网站都需要用到,很广泛(如:一般商城网、团购网、房产网、信息分类网站等等)。

希望达到的目的是:

①能够对多条件筛选功能有一个初步的认识。(起码自己做,也能够快速实现吧);

②对多条件筛选的实现中,数据库该如何去设计才会更优化和更合理些(参考前人的经验);

③对多条件筛选中的一些策略或者是技巧,能有一个了解(会总结几点)

二、然后,我们首先看一下现在需求是如何的?(多条件筛选,请参考京东、苏宁、国美等)

①京东商城的商品筛选功能(截图):

【PHP开源产品】Ecshop的商品筛选功能实现分析之一

②苏宁商城的商品筛选功能(截图)

【PHP开源产品】Ecshop的商品筛选功能实现分析之一

③国美在线的商品筛选功能(截图)

【PHP开源产品】Ecshop的商品筛选功能实现分析之一

补充:其实,要对该功能的观察,还必须对地址栏,也作一番思考的,我这里就省略了,毕竟如果这样分析,会更简单一些。上面这些例子,只作一个引子吧。后续我会完善它的。

④ECSHOP的商品筛选功能实现,展示细节(截图+文字)如下:

1)首先,我本地服务器,给本机安装的ecshop演示网站,配了一个虚拟主机地址:为 http://demo.ecshop.com

2)然后,我就通过该地址来访问主页,并查看属于导航栏中“GSM手机”分类下的商品。如下:

访问地址为:http://demo.ecshop.com/category.php?id=3

结果页面为:

【PHP开源产品】Ecshop的商品筛选功能实现分析之一

那么,此时的访问,会罗列出,属于“GSM手机”分类下(即cat_id=3)的所有商品,因为目前还没有对商品进行多筛选。

访问地址为: http://demo.ecshop.com/category.php?id=3&brand=1&price_min=0&price_max=0

结果页面为:

【PHP开源产品】Ecshop的商品筛选功能实现分析之一

如果我选择了多条件搜索,看截图:

访问地址为: http://demo.ecshop.com/category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.0.160.0

结果页面为:

【PHP开源产品】Ecshop的商品筛选功能实现分析之一

分析:从上面的地址栏的变化和截图的变化,可以初步看出ecshop的多条件搜索方法是怎么样的了。

猜想1:随着用户每一次点击条件(属性)进行商品筛选时,搜索地址栏会变化,为什么地址栏会变化,

从而我们知道,这个属性的超链接,是动态生成的。ecshop后台一定是作了大量的判断过程,并最终为每一个商品属性,生成一个超链接地址,以防止上一次的搜索条件丢失。

猜想2:商品的价格区间,是根据什么来计算的呢?还是人工后台设置的,京东的商品价格区间,好像都是很有规律的,和ecshop的价格区间,有比较大的区别。别管这么多了,我们还是先研究ecshop的再说。

猜想3:参数 filter_attr=163.0.160.0 中的几个数字,一定代表着下面:颜色、屏幕大小 、手机制式、外观样式属性下,都选择了哪一些值了。

----> 带着这样一些疑问,那我们直接去研究一下ecshop是如何实现上面的多条件搜索功能啦。。。GO。。。

首先,我们到ecshop的网站更目录,找到category.php文件,打开它进行研究一下。

1.点击这里,查看全部代码的分析过程。

【PHP开源产品】Ecshop的商品筛选功能实现分析之一【PHP开源产品】Ecshop的商品筛选功能实现分析之一

  1 php
  2 /**
  3  * 分析首页 商品分类页面category.php的实现方法
  4  * ECSHOP 2.7.2 商品分类
  5  */
  6 define('IN_ECS', true);
  7 require(dirname(__FILE__) . '/includes/init.php');
  8 if ((DEBUG_MODE & 2) != 2) {
  9     $smarty->caching = true;
 10 }
 11 
 12 //====> 请求地址栏:http://localhost/category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.160.186
 13 //====> 1.获取分类id
 14 /* 获得请求的分类 ID */
 15 if (isset($_REQUEST['id'])) {
 16     $cat_id = intval($_REQUEST['id']);
 17 }
 18 elseif (isset($_REQUEST['category'])) {
 19     $cat_id = intval($_REQUEST['category']);
 20 } else {
 21     /* 如果分类ID为0,则返回首页 */
 22     ecs_header("Location: ./\n");
 23     exit;
 24 }
 25 
 26 //====> 2.首先获取所有GET和POST中,可用于搜索的参数的值。
 27 //====> 若没有此参数,则说明,并没有用此参数来搜索,默认要给它一个默认值。
 28 /* 初始化分页信息 */
 29 $page = isset($_REQUEST['page'])   && intval($_REQUEST['page'])  > 0 ? intval($_REQUEST['page'])  : 1;
 30 $size = isset($_CFG['page_size'])  && intval($_CFG['page_size']) > 0 ? intval($_CFG['page_size']) : 10;
 31 $brand = isset($_REQUEST['brand']) && intval($_REQUEST['brand']) > 0 ? intval($_REQUEST['brand']) : 0;
 32 $price_max = isset($_REQUEST['price_max']) && intval($_REQUEST['price_max']) > 0 ? intval($_REQUEST['price_max']) : 0;
 33 $price_min = isset($_REQUEST['price_min']) && intval($_REQUEST['price_min']) > 0 ? intval($_REQUEST['price_min']) : 0;
 34 $filter_attr_str = isset($_REQUEST['filter_attr']) ? htmlspecialchars(trim($_REQUEST['filter_attr'])) : '0';
 35 $filter_attr_str = urldecode($filter_attr_str);
 36 $filter_attr = empty($filter_attr_str) ? '' : explode('.', trim($filter_attr_str));
 37 
 38 /* 排序、显示方式以及类型 */
 39 $default_display_type = $_CFG['show_order_type'] == '0' ? 'list' : ($_CFG['show_order_type'] == '1' ? 'grid' : 'text');
 40 $default_sort_order_method = $_CFG['sort_order_method'] == '0' ? 'DESC' : 'ASC';
 41 $default_sort_order_type   = $_CFG['sort_order_type'] == '0' ? 'goods_id' : ($_CFG['sort_order_type'] == '1' ? 'shop_price' : 'last_update');
 42 
 43 $sort  = (isset($_REQUEST['sort'])  && in_array(trim(strtolower($_REQUEST['sort'])), array('goods_id', 'shop_price', 'last_update'))) ? trim($_REQUEST['sort'])  : $default_sort_order_type;
 44 $order = (isset($_REQUEST['order']) && in_array(trim(strtoupper($_REQUEST['order'])), array('ASC', 'DESC')))                              ? trim($_REQUEST['order']) : $default_sort_order_method;
 45 $display  = (isset($_REQUEST['display']) && in_array(trim(strtolower($_REQUEST['display'])), array('list', 'grid', 'text'))) ? trim($_REQUEST['display'])  : (isset($_COOKIE['ECS']['display']) ? $_COOKIE['ECS']['display'] : $default_display_type);
 46 $display  = in_array($display, array('list', 'grid', 'text')) ? $display : 'text';
 47 setcookie('ECS[display]', $display, gmtime() + 86400 * 7);
 48 
 49 /* 页面的缓存ID */
 50 $cache_id = sprintf('%X', crc32($cat_id . '-' . $display . '-' . $sort  .'-' . $order  .'-' . $page . '-' . $size . '-' . $_SESSION['user_rank'] . '-' .
 51         $_CFG['lang'] .'-'. $brand. '-' . $price_max . '-' .$price_min . '-' . $filter_attr_str));
 52 
 53 /* 如果页面没有被缓存则重新获取页面的内容 */
 54 if (!$smarty->is_cached('category.dwt', $cache_id)) {
 55 //====> 3.把该栏目下的所有子栏目id获取,如果没有子栏目,则是自身。
 56 //===> TABLE:ecs_category
 57 //====> RETURN:id=3 => g.cat_id IN ('3')  或 id=6 => g.cat_id IN ('6','8','9','11','7')
 58 //====> 说明:ecshop的特点是,*栏目下也可以添加商品,所以会把*栏目id也放在数组里面
 59 $children = get_children($cat_id);  
 60 
 61 //====> 4.获得该分类的相关信息。如:分类名称、价格分级、筛选属性、父id
 62 //===> TABLE:ecs_category
 63 //====> RETURN:Array ( [cat_name] => GSM手机 [keywords] => [cat_desc] => [style] => [grade] => 4 [filter_attr] => 185,189,173,178 [parent_id] => 1 )
 64 $cat = get_cat_info($cat_id);   
 65 
 66 //===> 5.如果有品牌筛选brand参数,那么获取出该品牌名称
 67 //===> TABLE:ecs_brand
 68 //===> SQL:SELECT brand_name FROM `ecshop`.`ecs_brand` WHERE brand_id = '1'
 69 //====> RETURN:诺基亚
 70  /* 赋值固定内容 */
 71 if ($brand > 0) {
 72     $sql = "SELECT brand_name FROM " .$GLOBALS['ecs']->table('brand'). " WHERE brand_id = '$brand'";
 73     $brand_name = $db->getOne($sql);
 74 } else {
 75     $brand_name = '';
 76 }
 77 
 78 
 79 ///>>================开始---商品价格区间处理==================>>///
 80 //===> 6.获取该分类cat的价格分级:
 81 //===> ①如果价格分级=0,那么获取直接父类的价格分级。(Ⅰ如果父类价格也=0,那么就是不用价格分级 )
 82 //===> ②如果价格分级!=0,那么就是自身的价格分级。
 83 //===> TABLE:ecs_category
 84 //===> RETURN:$cat['grade']=4
 85 /* 获取价格分级 */
 86 if ($cat['grade'] == 0  && $cat['parent_id'] != 0) { //  ==>如果价格分级为空,但是它还有上级分类,那么取直接上级的价格分级等数
 87     $cat['grade'] = get_parent_grade($cat_id); //如果当前分类级别为空,取最近的上级分类
 88 }
 89 
 90 //===> 7.对价格区间进行划分。 ecshop的划分方法,是根据算法来的,比较复杂。
 91 //===> 如果价格分级>1,那么就执行价格区间划分
 92 if ($cat['grade'] > 1) {
 93     /* 需要价格分级 */
 94 
 95     /*
 96      算法思路:
 97     1、当分级大于1时,进行价格分级
 98     2、取出该类下商品价格的最大值、最小值
 99     3、根据商品价格的最大值来计算商品价格的分级数量级:
100     价格范围(不含最大值)    分级数量级
101     0-0.1                   0.001
102     0.1-1                   0.01
103     1-10                    0.1
104     10-100                  1
105     100-1000                10
106     1000-10000              100
107     4、计算价格跨度:
108     取整((最大值-最小值) / (价格分级数) / 数量级) * 数量级
109     5、根据价格跨度计算价格范围区间
110     6、查询数据库
111 
112     可能存在问题:
113     1、
114     由于价格跨度是由最大值、最小值计算出来的
115     然后再通过价格跨度来确定显示时的价格范围区间
116     所以可能会存在价格分级数量不正确的问题
117     该问题没有证明
118     2、
119     当价格=最大值时,分级会多出来,已被证明存在
120     */
121     
122     //===> 获得当前分类下商品价格的最大值、最小值
123     //===> 获得所有扩展分类属于指定分类的所有商品ID ,其中goods_id = 16的商品的扩展属于分类3
124     //===> TABLE:ecs_goods 和 ecs_goods_cat
125     //===> SQL:SELECT min(g.shop_price) AS min, max(g.shop_price) as max FROM `ecshop`.`ecs_goods` AS g WHERE (g.cat_id IN ('5') OR g.goods_id IN ('8','16') ) AND g.is_delete = 0 AND g.is_on_sale = 1 AND g.is_alone_sale = 1
126     //===> RETURN:Array ( [min] => 280.00 [max] => 5999.00 ) 
127     $sql = "SELECT min(g.shop_price) AS min, max(g.shop_price) as max ".
128             " FROM " . $ecs->table('goods'). " AS g ".
129             " WHERE ($children OR " . get_extension_goods($children) . ') AND g.is_delete = 0 AND g.is_on_sale = 1 AND g.is_alone_sale = 1  ';
130     $row = $db->getRow($sql);
131 
132     
133     //===> 按照公式计算出最低价格和最高价格、以及价格跨度
134     //===============↓↓↓先不做讨论===============
135     // 取得价格分级最小单位级数,比如,千元商品最小以100为级数
136     $price_grade = 0.0001;
137     for($i=-2; $ilog10