一、首先,说明一下为什么要对category.php文件进行分析。 原因如下: ①个人对商城类商品筛选功能的实现比较好奇; ②对商城中关于商品的数据表设计比较感兴趣。(该功能涉及到与数据库的交互,而且与数据库中数据表的设计好坏有一定的联系); ③多条件(
一、首先,说明一下为什么要对category.php文件进行分析。原因如下:
①个人对商城类商品筛选功能的实现比较好奇;
②对商城中关于商品的数据表设计比较感兴趣。(该功能涉及到与数据库的交互,而且与数据库中数据表的设计好坏有一定的联系);
③多条件(属性)筛选功能在现今的很多网站都需要用到,很广泛(如:一般商城网、团购网、房产网、信息分类网站等等)。
希望达到的目的是:
①能够对多条件筛选功能有一个初步的认识。(起码自己做,也能够快速实现吧);
②对多条件筛选的实现中,数据库该如何去设计才会更优化和更合理些(参考前人的经验);
③对多条件筛选中的一些策略或者是技巧,能有一个了解(会总结几点)
二、然后,我们首先看一下现在需求是如何的?(多条件筛选,请参考京东、苏宁、国美等)①京东商城的商品筛选功能(截图):
②苏宁商城的商品筛选功能(截图)
③国美在线的商品筛选功能(截图)
补充:其实,要对该功能的观察,还必须对地址栏,也作一番思考的,我这里就省略了,毕竟如果这样分析,会更简单一些。上面这些例子,只作一个引子吧。后续我会完善它的。
④ecshop的商品筛选功能实现,展示细节(截图+文字)如下:
1)首先,我本地服务器,给本机安装的ecshop演示网站,配了一个虚拟主机地址:为 http://demo.ecshop.com
2)然后,我就通过该地址来访问主页,并查看属于导航栏中“gsm手机”分类下的商品。如下:
访问地址为:http://demo.ecshop.com/category.php?id=3
结果页面为:
那么,此时的访问,会罗列出,属于“gsm手机”分类下(即cat_id=3)的所有商品,因为目前还没有对商品进行多筛选。
访问地址为: http://demo.ecshop.com/category.php?id=3&brand=1&price_min=0&price_max=0
结果页面为:
如果我选择了多条件搜索,看截图:
访问地址为: http://demo.ecshop.com/category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.0.160.0
结果页面为:
分析:从上面的地址栏的变化和截图的变化,可以初步看出ecshop的多条件搜索方法是怎么样的了。
猜想1:随着用户每一次点击条件(属性)进行商品筛选时,搜索地址栏会变化,为什么地址栏会变化,
从而我们知道,这个属性的超链接,是动态生成的。ecshop后台一定是作了大量的判断过程,并最终为每一个商品属性,生成一个超链接地址,以防止上一次的搜索条件丢失。
猜想2:商品的价格区间,是根据什么来计算的呢?还是人工后台设置的,京东的商品价格区间,好像都是很有规律的,和ecshop的价格区间,有比较大的区别。别管这么多了,我们还是先研究ecshop的再说。
猜想3:参数 filter_attr=163.0.160.0 中的几个数字,一定代表着下面:颜色、屏幕大小 、手机制式、外观样式属性下,都选择了哪一些值了。
----> 带着这样一些疑问,那我们直接去研究一下ecshop是如何实现上面的多条件搜索功能啦。。。go。。。
首先,我们到ecshop的网站更目录,找到category.php文件,打开它进行研究一下。
1.点击这里,查看全部代码的分析过程。
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.001102 0.1-1 0.01103 1-10 0.1104 10-100 1105 100-1000 10106 1000-10000 100107 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的商品的扩展属于分类3124 //===> table:ecs_goods 和 ecs_goods_cat125 //===> 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 = 1126 //===> 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($row['max']); $i++) {138 $price_grade *= 10;139 }140 141 //价格跨度142 $dx = ceil(($row['max'] - $row['min']) / ($cat['grade']) / $price_grade) * $price_grade;143 if($dx == 0) {144 $dx = $price_grade;145 }146 147 for($i = 1; $row['min'] > $dx * $i; $i ++);148 149 for($j = 1; $row['min'] > $dx * ($i-1) + $price_grade * $j; $j++);150 $row['min'] = $dx * ($i-1) + $price_grade * ($j - 1);151 152 for(; $row['max'] >= $dx * $i; $i ++);153 $row['max'] = $dx * ($i) + $price_grade * ($j - 1);154 155 //===>这里打印最高价格和最低价格:$row=>array ( [min] => 200 [max] => 6200 ) 156 //===>这里打印价格跨度:$dx = 1500157 158 //===============先不做讨论↑↑↑==================//159 160 161 //===> 根据商品的价格、价格区间的最低价格、以及价格跨度,计算该商品价格属于哪一个区间内。162 //===> 获取属于该价格区间内的商品的数量、获取sn则属于哪一个区间sn=0为200-1700、sn=1为1700-3200、sn=3为4700-6200。163 //===> 因为没有商品价格属于第二区间,则不存在sn=2,那么3200-4700则没有任何商品164 //===> table:ecs_goods 和 ecs_goods_cat165 //===> sql:select (floor((g.shop_price - 200) / 1500)) as sn, count(*) as goods_num from `ecshop`.`ecs_goods` as g where (g.cat_id in ('3') or g.goods_id in ('16') ) and g.is_delete = 0 and g.is_on_sale = 1 and g.is_alone_sale = 1 group by sn 166 //===>return:array ( [0] => array ( [sn] => 0 [goods_num] => 6 ) [1] => array ( [sn] => 1 [goods_num] => 5 ) [2] => array ( [sn] => 3 [goods_num] => 1 ) ) 167 $sql = select (floor((g.shop_price - $row[min]) / $dx)) as sn, count(*) as goods_num .168 from . $ecs->table('goods') . as g .169 where ($children or . get_extension_goods($children) . ') and g.is_delete = 0 and g.is_on_sale = 1 and g.is_alone_sale = 1 '.170 group by sn ;171 $price_grade = $db->getall($sql);172 173 174 //===> 根据价格等级price_grade,计算真正的价格区间。175 //===> 方法build_uri()重要:要为每一个价格区间,生成一个url超链接,以备前台点击搜索用176 //===> 并根据价格参数,判断该区间,是否是当前被搜索的区间177 //===> 把价格区间,注入模板,供前台使用178 //===> return:array ( [0] => array ( [sn] => 0 [goods_num] => 6 [start] => 0 [end] => 0 [price_range] => 全部 [url] => category.php?id=3&brand=1&price_min=0&price_max=0&filter_attr=163.216.160.186 [selected] => 0 ) [1] => array ( [sn] => 1 [goods_num] => 6 [start] => 200 [end] => 1700 [price_range] => 200 - 1700 [formated_start] => ¥200元 [formated_end] => ¥1700元 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 1 ) [2] => array ( [sn] => 3 [goods_num] => 5 [start] => 1700 [end] => 3200 [price_range] => 1700 - 3200 [formated_start] => ¥1700元 [formated_end] => ¥3200元 [url] => category.php?id=3&brand=1&price_min=1700&price_max=3200&filter_attr=163.216.160.186 [selected] => 0 ) [3] => array ( [goods_num] => 1 [start] => 4700 [end] => 6200 [price_range] => 4700 - 6200 [formated_start] => ¥4700元 [formated_end] => ¥6200元 [url] => category.php?id=3&brand=1&price_min=4700&price_max=6200&filter_attr=163.216.160.186 [selected] => 0 ) ) 179 foreach ($price_grade as $key=>$val) {180 $temp_key = $key + 1;181 $price_grade[$temp_key]['goods_num'] = $val['goods_num'];182 $price_grade[$temp_key]['start'] = $row['min'] + round($dx * $val['sn']);183 $price_grade[$temp_key]['end'] = $row['min'] + round($dx * ($val['sn'] + 1));184 $price_grade[$temp_key]['price_range'] = $price_grade[$temp_key]['start'] . ' - ' . $price_grade[$temp_key]['end'];185 $price_grade[$temp_key]['formated_start'] = price_format($price_grade[$temp_key]['start']);186 $price_grade[$temp_key]['formated_end'] = price_format($price_grade[$temp_key]['end']);187 $price_grade[$temp_key]['url'] = build_uri('category', array('cid'=>$cat_id, 'bid'=>$brand, 'price_min'=>$price_grade[$temp_key]['start'], 'price_max'=> $price_grade[$temp_key]['end'], 'filter_attr'=>$filter_attr_str), $cat['cat_name']);188 /* 判断价格区间是否被选中 */189 if (isset($_request['price_min']) && $price_grade[$temp_key]['start'] == $price_min && $price_grade[$temp_key]['end'] == $price_max) {190 $price_grade[$temp_key]['selected'] = 1;191 }192 else {193 $price_grade[$temp_key]['selected'] = 0;194 }195 }196 //补充一个选择全部的类型的数组197 $price_grade[0]['start'] = 0;198 $price_grade[0]['end'] = 0;199 $price_grade[0]['price_range'] = $_lang['all_attribute'];200 $price_grade[0]['url'] = build_uri('category', array('cid'=>$cat_id, 'bid'=>$brand, 'price_min'=>0, 'price_max'=> 0, 'filter_attr'=>$filter_attr_str), $cat['cat_name']);201 $price_grade[0]['selected'] = empty($price_max) ? 1 : 0;202 //把价格区间数组,注入模板文件203 $smarty->assign('price_grade', $price_grade);204 }205 ///206 207 208 ///>>================开始---商品品牌处理==================>>///209 //====> ?db_create_in(array_unique(array_merge(array($cat_id), array_keys(cat_list($cat_id, 0, false))))) . )210 //===> 品牌筛选功能:把该栏目下(其中包括扩展分类下的商品),所有商品的品牌获取,但不能重复。211 //===> 品牌下有商品并且商品状态正常,才能把该品牌取出。没有商品的品牌不能取出212 //===> table:ecs_brand、ecs_goods 和 ecs_goods_cat213 //===> sql:select b.brand_id, b.brand_name, count(*) as goods_num from `ecshop`.`ecs_brand`as b, `ecshop`.`ecs_goods` as g left join `ecshop`.`ecs_goods_cat` as gc on g.goods_id = gc.goods_id where g.brand_id = b.brand_id and (g.cat_id in ('3') or gc.cat_id in ('3') ) and b.is_show = 1 and g.is_on_sale = 1 and g.is_alone_sale = 1 and g.is_delete = 0 group by b.brand_id having goods_num > 0 order by b.sort_order, b.brand_id asc214 //===> return:array ( [0] => array ( [brand_id] => 1 [brand_name] => 诺基亚 [goods_num] => 3 ) [1] => array ( [brand_id] => 2 [brand_name] => 摩托罗拉 [goods_num] => 1 ) [2] => array ( [brand_id] => 3 [brand_name] => 多普达 [goods_num] => 1 ) [3] => array ( [brand_id] => 4 [brand_name] => 飞利浦 [goods_num] => 2 ) [4] => array ( [brand_id] => 5 [brand_name] => 夏新 [goods_num] => 1 ) [5] => array ( [brand_id] => 6 [brand_name] => 三星 [goods_num] => 2 ) [6] => array ( [brand_id] => 7 [brand_name] => 索爱 [goods_num] => 1 ) [7] => array ( [brand_id] => 9 [brand_name] => 联想 [goods_num] => 1 ) [8] => array ( [brand_id] => 10 [brand_name] => 金立 [goods_num] => 1 ) ) 215 /* 品牌筛选 */216 $sql = select b.brand_id, b.brand_name, count(*) as goods_num .217 from . $globals['ecs']->table('brand') . as b, .218 $globals['ecs']->table('goods') . as g left join . $globals['ecs']->table('goods_cat') . as gc on g.goods_id = gc.goods_id .219 where g.brand_id = b.brand_id and ($children or . 'gc.cat_id ' . db_create_in(array_unique(array_merge(array($cat_id), array_keys(cat_list($cat_id, 0, false))))) . ) and b.is_show = 1 .220 and g.is_on_sale = 1 and g.is_alone_sale = 1 and g.is_delete = 0 .221 group by b.brand_id having goods_num > 0 order by b.sort_order, b.brand_id asc;222 $brands = $globals['db']->getall($sql);223 224 //===> 把该分类下所有商品的品牌,组合成数组,给前台调用225 //===> 方法build_uri()重要:要为每品牌,生成一个url超链接,以备前台点击搜索用226 //===> 把数组注入模板文件227 //====! 这样一种组织数组的方式,有它的缺陷:就是说不能够把每一个品牌下面的,商品的数量保存下来,同时也会有一些无用的数据,掺杂其中。228 //return:array ( [0] => array ( [brand_id] => 1 [brand_name] => 全部 [goods_num] => 3 [url] => category.php?id=3&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 0 ) [1] => array ( [brand_id] => 2 [brand_name] => 诺基亚 [goods_num] => 1 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 1 ) [2] => array ( [brand_id] => 3 [brand_name] => 摩托罗拉 [goods_num] => 1 [url] => category.php?id=3&brand=2&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 0 ) [3] => array ( [brand_id] => 4 [brand_name] => 多普达 [goods_num] => 2 [url] => category.php?id=3&brand=3&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 0 ) [4] => array ( [brand_id] => 5 [brand_name] => 飞利浦 [goods_num] => 1 [url] => category.php?id=3&brand=4&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 0 ) [5] => array ( [brand_id] => 6 [brand_name] => 夏新 [goods_num] => 2 [url] => category.php?id=3&brand=5&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 0 ) [6] => array ( [brand_id] => 7 [brand_name] => 三星 [goods_num] => 1 [url] => category.php?id=3&brand=6&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 0 ) [7] => array ( [brand_id] => 9 [brand_name] => 索爱 [goods_num] => 1 [url] => category.php?id=3&brand=7&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 0 ) [8] => array ( [brand_id] => 10 [brand_name] => 联想 [goods_num] => 1 [url] => category.php?id=3&brand=9&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 0 ) [9] => array ( [brand_name] => 金立 [url] => category.php?id=3&brand=10&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 0 ) ) 229 foreach ($brands as $key => $val) {230 $temp_key = $key + 1;231 $brands[$temp_key]['brand_name'] = $val['brand_name'];232 $brands[$temp_key]['url'] = build_uri('category', array('cid' => $cat_id, 'bid' => $val['brand_id'], 'price_min'=>$price_min, 'price_max'=> $price_max, 'filter_attr'=>$filter_attr_str), $cat['cat_name']);233 /* 判断品牌是否被选中 */234 if ($brand == $brands[$key]['brand_id']) {235 $brands[$temp_key]['selected'] = 1;236 } else {237 $brands[$temp_key]['selected'] = 0;238 }239 }240 //补充一个选择全部品牌的数组241 $brands[0]['brand_name'] = $_lang['all_attribute'];242 $brands[0]['url'] = build_uri('category', array('cid' => $cat_id, 'bid' => 0, 'price_min'=>$price_min, 'price_max'=> $price_max, 'filter_attr'=>$filter_attr_str), $cat['cat_name']);243 $brands[0]['selected'] = empty($brand) ? 1 : 0;244 //把品牌数组注入模板245 $smarty->assign('brands', $brands);246 ///247 248 249 250 ///>>==================开始---商品属性处理==================>>///251 /* 属性筛选 */252 $ext = ''; //商品查询条件扩展253 if ($cat['filter_attr'] > 0) {254 //===>需要筛选的属性,是人工在后台添加的,存放在ecs_category 表的中 filter_attr字段里面,格式如:185,189,120,190255 //===>return:array ( [0] => 185 [1] => 189 [2] => 173 [3] => 178 )256 $cat_filter_attr = explode(',', $cat['filter_attr']); //提取出此分类的筛选属性257 258 //===> 然后,对每一个属性(此属性是主属性,下面还有子属性),循环进行操作259 //===> ①获取该属性的属性名260 //===> ②获取该属性的所有子属性(子属性必须是有被商品在使用的,不然,不要将其显示出来)261 //===> 意义:因为该属性涉及到搜索参数,如果该属性下本身没有商品,那么就没有显示出来的意义了。262 //===> return: array ( [0] => array ( [attr_id] => 185 [goods_id] => 167 [attr_value] => 灰色 ) [1] => array ( [attr_id] => 185 [goods_id] => 198 [attr_value] => 白色 ) [2] => array ( [attr_id] => 185 [goods_id] => 197 [attr_value] => 金色 ) [3] => array ( [attr_id] => 185 [goods_id] => 163 [attr_value] => 黑色 ) ) 263 $all_attr_list = array();264 foreach ($cat_filter_attr as $key => $value) { 265 $sql = select a.attr_name from . $ecs->table('attribute') . as a, . $ecs->table('goods_attr') . as ga, . $ecs->table('goods') . as g where ($children or . get_extension_goods($children) . ) and a.attr_id = ga.attr_id and g.goods_id = ga.goods_id and g.is_delete = 0 and g.is_on_sale = 1 and g.is_alone_sale = 1 and a.attr_id='$value';266 if($temp_name = $db->getone($sql)) {267 //获取该属性名(主属性)268 $all_attr_list[$key]['filter_attr_name'] = $temp_name; 269 270 //获取该属性的所有子属性(子属性必须是有被商品在使用的,不然,不要将其显示出来)271 //return: array ( [0] => array ( [attr_id] => 185 [goods_id] => 167 [attr_value] => 灰色 ) [1] => array ( [attr_id] => 185 [goods_id] => 198 [attr_value] => 白色 ) [2] => array ( [attr_id] => 185 [goods_id] => 197 [attr_value] => 金色 ) [3] => array ( [attr_id] => 185 [goods_id] => 163 [attr_value] => 黑色 ) )272 $sql = select a.attr_id, min(a.goods_attr_id ) as goods_id, a.attr_value as attr_value from . $ecs->table('goods_attr') . as a, . $ecs->table('goods') .273 as g .274 where ($children or . get_extension_goods($children) . ') and g.goods_id = a.goods_id and g.is_delete = 0 and g.is_on_sale = 1 and g.is_alone_sale = 1 '.275 and a.attr_id='$value' .276 group by a.attr_value;277 $attr_list = $db->getall($sql);278 279 //如果后台指定该分类下,用于搜索的属性的组数,是跟地址栏中filter_attr=0.0.0.0 中0的个数是一样的,而且顺序都是一样的280 //第一个0,表示第一组属性中,它选择了哪一个子属性,以此类推281 //获取当前url中已选择属性的值,并保留在数组中282 //!这里要作循环,是因为避免属性为0或者空时,导致出错,因为直接把$filter_attr 赋值给 $temp_arrt_url_arr会出错。283 //return:array ( [0] => 163 [1] => 216 [2] => 160 [3] => 186 ) 284 $temp_arrt_url_arr = array();285 for ($i = 0; $i count($cat_filter_attr); $i++) { 286 $temp_arrt_url_arr[$i] = !empty($filter_attr[$i]) ? $filter_attr[$i] : 0; 287 }288 289 //这里是该属性下,选择全部时的数组形式290 //哪一个属性的值为0,即说明用户选择的是该属性的全部选项291 //为“全部”生成url292 //data:array ( [0] => 0 [1] => 216 [2] => 160 [3] => 186 ) 293 $temp_arrt_url_arr[$key] = 0; //“全部”的信息生成 294 $temp_arrt_url = implode('.', $temp_arrt_url_arr);295 $all_attr_list[$key]['attr_list'][0]['attr_value'] = $_lang['all_attribute'];296 $all_attr_list[$key]['attr_list'][0]['url'] = build_uri('category', array('cid'=>$cat_id, 'bid'=>$brand, 'price_min'=>$price_min, 'price_max'=>$price_max, 'filter_attr'=>$temp_arrt_url), $cat['cat_name']);297 $all_attr_list[$key]['attr_list'][0]['selected'] = empty($filter_attr[$key]) ? 1 : 0;298 299 //循环计算子属性的相关数组:属性值,属性是否选择,属性的url300 //判断当前子属性,是否被选中状态301 //return:array ( [0] => array ( [filter_attr_name] => 颜色 [attr_list] => array ( [0] => array ( [attr_value] => 全部 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=0.216.160.186 [selected] => 0 ) [1] => array ( [attr_value] => 灰色 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=167.216.160.186 [selected] => 0 ) [2] => array ( [attr_value] => 白色 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=198.216.160.186 [selected] => 0 ) [3] => array ( [attr_value] => 金色 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=197.216.160.186 [selected] => 0 ) [4] => array ( [attr_value] => 黑色 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 1 ) ) ) [1] => array ( [filter_attr_name] => 屏幕大小 [attr_list] => array ( [0] => array ( [attr_value] => 全部 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.0.160.186 [selected] => 0 ) [1] => array ( [attr_value] => 1.75英寸 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.229.160.186 [selected] => 0 ) [2] => array ( [attr_value] => 2.0英寸 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 1 ) [3] => array ( [attr_value] => 2.2英寸 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.223.160.186 [selected] => 0 ) [4] => array ( [attr_value] => 2.6英寸 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.156.160.186 [selected] => 0 ) [5] => array ( [attr_value] => 2.8英寸 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.200.160.186 [selected] => 0 ) ) ) [2] => array ( [filter_attr_name] => 手机制式 [attr_list] => array ( [0] => array ( [attr_value] => 全部 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.0.186 [selected] => 0 ) [1] => array ( [attr_value] => cdma [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.202.186 [selected] => 0 ) [2] => array ( [attr_value] => gsm,850,900,1800,1900 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 1 ) [3] => array ( [attr_value] => gsm,900,1800,1900,2100 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.195.186 [selected] => 0 ) ) ) [3] => array ( [filter_attr_name] => 外观样式 [attr_list] => array ( [0] => array ( [attr_value] => 全部 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.160.0 [selected] => 0 ) [1] => array ( [attr_value] => 滑盖 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.160.199 [selected] => 0 ) [2] => array ( [attr_value] => 直板 [url] => category.php?id=3&brand=1&price_min=200&price_max=1700&filter_attr=163.216.160.186 [selected] => 1 ) ) ) ) 302 foreach ($attr_list as $k => $v) {303 $temp_key = $k + 1;304 $temp_arrt_url_arr[$key] = $v['goods_id']; //为url中代表当前筛选属性的位置变量赋值,并生成以‘.’分隔的筛选属性字符串305 $temp_arrt_url = implode('.', $temp_arrt_url_arr);306 307 $all_attr_list[$key]['attr_list'][$temp_key]['attr_value'] = $v['attr_value'];308 $all_attr_list[$key]['attr_list'][$temp_key]['url'] = build_uri('category', array('cid'=>$cat_id, 'bid'=>$brand, 'price_min'=>$price_min, 'price_max'=>$price_max, 'filter_attr'=>$temp_arrt_url), $cat['cat_name']);309 310 if (!empty($filter_attr[$key]) and $filter_attr[$key] == $v['goods_id']) { //处理已被选择的子属性311 $all_attr_list[$key]['attr_list'][$temp_key]['selected'] = 1;312 }313 else {314 $all_attr_list[$key]['attr_list'][$temp_key]['selected'] = 0;315 }316 }317 }318 }319 //为模板注入变量320 $smarty->assign('filter_attr_list', $all_attr_list);321 322 323 /* 扩展商品查询条件 */324 if (!empty($filter_attr)) {325 $ext_sql = select distinct(b.goods_id) from . $ecs->table('goods_attr') . as a, . $ecs->table('goods_attr') . as b . where ;326 $ext_group_goods = array();327 foreach ($filter_attr as $k => $v) { // 查出符合所有筛选属性条件的商品id */328 if (is_numeric($v) && $v !=0 ) {329 $sql = $ext_sql . b.attr_value = a.attr_value and b.attr_id = . $cat_filter_attr[$k] . and a.goods_attr_id = . $v;330 $ext_group_goods = $db->getcolcached($sql);331 $ext .= ' and ' . db_create_in($ext_group_goods, 'g.goods_id');332 }333 }334 }335 } 336 ///337 338 //向模板,载入一些前台,必备的变量和常量339 assign_template('c', array($cat_id));340 341 //return:array ( [title] => 夏新_gsm手机_手机类型_ecshop演示站 - powered by ecshop [ur_here] => 首页 > 手机类型 > gsm手机 > 夏新 )342 $position = assign_ur_here($cat_id, $brand_name);343 344 $smarty->assign('page_title', $position['title']); // 页面标题345 $smarty->assign('ur_here', $position['ur_here']); // 当前位置346 347 $smarty->assign('categories', get_categories_tree($cat_id)); // 分类树348 $smarty->assign('helps', get_shop_help()); // 网店帮助349 $smarty->assign('top_goods', get_top10()); // 销售排行350 $smarty->assign('show_marketprice', $_cfg['show_marketprice']); //是否显示市场价351 $smarty->assign('category', $cat_id);352 $smarty->assign('brand_id', $brand);353 $smarty->assign('price_max', $price_max);354 $smarty->assign('price_min', $price_min);355 $smarty->assign('filter_attr', $filter_attr_str);356 $smarty->assign('feed_url', ($_cfg['rewrite'] == 1) ? feed-c$cat_id.xml : 'feed.php?cat=' . $cat_id); // rss url357 358 if ($brand > 0) {359 $arr['all'] = array('brand_id' => 0,360 'brand_name' => $globals['_lang']['all_goods'],361 'brand_logo' => '',362 'goods_num' => '',363 'url' => build_uri('category', array('cid'=>$cat_id), $cat['cat_name'])364 );365 } else {366 $arr = array();367 }368 369 $brand_list = array_merge($arr, get_brands($cat_id, 'category'));370 $smarty->assign('data_dir', data_dir); //网站data目录371 $smarty->assign('brand_list', $brand_list);372 $smarty->assign('promotion_info', get_promotion_info()); //获取推荐信息373 374 /* 调查 */375 $vote = get_vote();376 if (!empty($vote)) {377 $smarty->assign('vote_id', $vote['id']);378 $smarty->assign('vote', $vote['content']);379 }380 381 //获取最热销、推荐和最热卖商品382 $smarty->assign('best_goods', get_category_recommend_goods('best', $children, $brand, $price_min, $price_max, $ext));383 $smarty->assign('promotion_goods', get_category_recommend_goods('promote', $children, $brand, $price_min, $price_max, $ext));384 $smarty->assign('hot_goods', get_category_recommend_goods('hot', $children, $brand, $price_min, $price_max, $ext));385 //获取该前状态下,商品的数量386 $count = get_cagtegory_goods_count($children, $brand, $price_min, $price_max, $ext);387 //最大页数388 $max_page = ($count> 0) ? ceil($count / $size) : 1;389 if ($page > $max_page) {390 $page = $max_page;391 }392 393 //获取该栏目下的所有商品394 $goodslist = category_get_goods($children, $brand, $price_min, $price_max, $ext, $size, $page, $sort, $order);395 396 //判断选择了列表还是图片方式显示方式397 if($display == 'grid') {398 if(count($goodslist) % 2 != 0) {399 $goodslist[] = array();400 }401 }402 403 $smarty->assign('goods_list', $goodslist); //注入商品列表404 $smarty->assign('category', $cat_id); //注入分类id405 $smarty->assign('script_name', 'category'); //注入该脚本的名称406 407 assign_pager('category', $cat_id, $count, $size, $sort, $order, $page, '', $brand, $price_min, $price_max, $display, $filter_attr_str); // 分页408 assign_dynamic('category'); // 动态内容409 }410 $smarty->display('category.dwt', $cache_id);411 ?>
查看category.php全部代码
2.分步分析其实现过程:
1)第一步,首先,文件一开头,一定是接收前台页面发送过来的各种post和get参数了,这里主要还是指地址栏get方式传过来的参数了。
1 //====> 2.首先获取所有get和post中,可用于搜索的参数的值。 2 //====> 若没有此参数,则说明,并没有用此参数来搜索,默认要给它一个默认值。 3 /* 初始化分页信息 */ 4 $page = isset($_request['page']) && intval($_request['page']) > 0 ? intval($_request['page']) : 1; 5 $size = isset($_cfg['page_size']) && intval($_cfg['page_size']) > 0 ? intval($_cfg['page_size']) : 10; 6 $brand = isset($_request['brand']) && intval($_request['brand']) > 0 ? intval($_request['brand']) : 0; 7 $price_max = isset($_request['price_max']) && intval($_request['price_max']) > 0 ? intval($_request['price_max']) : 0; 8 $price_min = isset($_request['price_min']) && intval($_request['price_min']) > 0 ? intval($_request['price_min']) : 0; 9 $filter_attr_str = isset($_request['filter_attr']) ? htmlspecialchars(trim($_request['filter_attr'])) : '0';10 $filter_attr_str = urldecode($filter_attr_str);11 $filter_attr = empty($filter_attr_str) ? '' : explode('.', trim($filter_attr_str));12 13 /* 排序、显示方式以及类型 */14 $default_display_type = $_cfg['show_order_type'] == '0' ? 'list' : ($_cfg['show_order_type'] == '1' ? 'grid' : 'text');15 $default_sort_order_method = $_cfg['sort_order_method'] == '0' ? 'desc' : 'asc';16 $default_sort_order_type = $_cfg['sort_order_type'] == '0' ? 'goods_id' : ($_cfg['sort_order_type'] == '1' ? 'shop_price' : 'last_update');17 18 $sort = (isset($_request['sort']) && in_array(trim(strtolower($_request['sort'])), array('goods_id', 'shop_price', 'last_update'))) ? trim($_request['sort']) : $default_sort_order_type;19 $order = (isset($_request['order']) && in_array(trim(strtoupper($_request['order'])), array('asc', 'desc'))) ? trim($_request['order']) : $default_sort_order_method;20 $display = (isset($_request['display']) && in_array(trim(strtolower($_request['display'])), array('list', 'grid', 'text'))) ? trim($_request['display']) : (i