一、简单的单条件查询

工作都是从简单的开始,先从最简单的单表查询开始,这个一般用在首页以及一些比较独立的页面,只需要查找几个符合条件的产品展示出来即可,可以使用分页或者不使用分页。下面这个是产品控制器 ProductController 中的一个函数,用于简单的查询,比如199元专区就可以使用 getTypeSimPro('price=199');

/**简单的筛选条件分类产品,单表查询* @param string $sql 单表查询的SQL* @param int $countPerPage=16 每页商品数* @param string $orderBy='salseF DESC' 排序 默认销量阈值* @return array $res 产品二维数组*/
function getTypeSimPro($sql, $countPerPage = 16, $orderBy='salesF DESC'){//$sql = "SELECT ProductId,name,mainPic,priceN,priceVIP,isNew,isHot,sales FROM product WHERE ".$sql;$productM = M('product');    // 实例化Data数据对象       $where = $sql ? 'onSale=1 AND '.$sql : 'onSale=1';$tempSQL = $productM->field('ProductId,name,mainPic,priceN,priceVIP,isNew,isHot,sales,salesF')->where( $where )                        ->order( $orderBy );//    $res = $this->executeTempSQL($tempSQL, $countPerPage);$res = $tempSQL->select();    //演示不使用分页,直接返回结果集return $res;
}

二、使用分页

由于Thinkphp的自带Page分页类有些不太好用,所以我进行了一点小改造,可以进行传递配置参数修改页码显示的方式。这里的主要实现逻辑是:

1、利用同一个临时数据库对象 $tempSQL ,使计数和查询结果的条件保持一致,注意这里使用了对象克隆,因为TP中,一个Model执行完操作后会被初始化成原始的Model对象,参见 TP手册连贯操作说明>> 。

2、$_GET['p']是Page类默认的辨别当前页码的参数。Page类尤其里面的 show() 函数是经过我改造的,可以传递定制化页码导航栏参数。不定制也可以,就是页码导航有点太长。

3、这里的 count() 在后面多表查询的时候是有BUG的,后面再说。

/*** 执行分类和搜索中的SQL对象* @param TP.Model $tempSQL Thinkphp的Model对象* @param int $countPerPage=16 每页的产品数* @return array $res['nowP']当前页数  $res['totalP']总产品数  $res['links']分页栏HTML     $res['productList']产品二维数组* */
protected function executeTempSQL( $tempSQL, $countPerPage=16 ){$tempSQL2 = clone $tempSQL;        //对象复制,否则调用一次后第二次会被初始化成原始的M对象
//    print_r($tempSQL);$count = $tempSQL->count(); // 查询满足要求的总记录数,这里在多表查询时一定要以产品编号为限制条件
//  var_dump($count);
//    var_dump($tempSQL);$nowPage = isset($_GET['p'])?$_GET['p']:1;  //当前页        import('ORG.Util.Page');// 导入分页类$Page = new \Think\Page($count,$countPerPage);        // 实例化分页类 传入总记录数,每页数$list = $tempSQL2->page($nowPage.','.$Page->listRows)->select();        //查询结果集
//    var_dump($list);//分页导航的定制$showConfig = array('first'  => '首页','prev'   => '上一页','next'   => '下一页',
//        'last'   => '尾页',    //这个不行'rollPage' => 5,        //最多显示5页导航);$links = $Page->show( $showConfig );            // 分页显示输出//var_dump($links);//var_dump($list);$res['nowP'] = $nowPage;$res['totalP'] = $count;$res['links'] = $links;            //分页输出$res['productList'] = $list;    //数据集return $res;
}

三、多表查询功能概览

先来一张截图,要达到的筛选功能大概是这个样子的。

其中的数据库设计为:

product表:ProductId-产品ID、name-产品名、sort1-一级分类、sort2-二级分类、sort_brand-品牌分类、price-价格、onSale-上下架……等等
reserve表:ProductId-产品ID、color-颜色、size-尺码、reserve-库存
brand表:id-品牌ID、name-品牌名
tagpro表:Id-自增没实际用途、tagId-标签ID、ProductId-产品ID
tag表:Id-标签ID、tag_name-标签名

商品与品牌是多对一的关系,用字段做关联;商品与标签是多对多的关系,用表做关联。在上面展示的分类和搜索中,黑色导航栏、性别以及以后可能扩展的筛选项为标签联表查询,尺码为库存表联表查询。

四、SearchController控制器

定义了一个Search控制器,里面有下面几个方法:

function index() 方法是根据上面页面中的筛选选项拼装相应的SQL语句的,提交到ProductController去筛选出相关的产品;

function getCutURL($getKey, $CtrlName=CONTROLLER_NAME) 是为了给页面生成一系列切除了指定get值的URL地址的;

function pageCheck() 如果改变了筛选条件,则去除页码参数,回到从第一页开始;

在我的项目规划中IndexController负责页面的显示,所以IndexController中的 search() 方法则负责搜索页面的展示,代码如下

    function search(){        $searchC = A('search');$res = $searchC->index(); $URLArr['type2URL'] = $searchC->getCutURL('type2');            //取消选择分类的URL$URLArr['brandURL'] = $searchC->getCutURL('brand');            //取消选择品牌的URL$URLArr['peopleURL'] = $searchC->getCutURL('people');        //取消选择性别人群的URL$URLArr['sizeURL'] = $searchC->getCutURL('size');            //取消选择尺码的URL$URLArr['priceURL'] = $searchC->getCutURL('price');            //取消选择价格区间的URL        $URLArr['keyword'] = $searchC->getCutURL('keyword');        //取消搜索关键字的URL    $URLArr['orderbyURL'] = $searchC->getCutURL('orderby');        //orderby按钮的URL前部分$this->assign('URLArr', $URLArr)        ->assign ( 'productArr', $res['productList'] )->assign( 'totalNum', $res['totalP'] )->assign( 'pageinfo', $res['links'])->display();}

五、两表多次查询

因为产品与标签是多对多的关系,所以有一种需求是:查询同时拥有两个标签一个产品,姑且设读取列为*即全部列。

一开始想到的SQL语句是这个样子的

SELECT * FROM product p INNER JOIN tagpro ON tagpro.ProductId = p.ProductId INNER JOIN tagpro ON tagpro.ProductId = p.ProductId WHERE onSale=1 AND tagpro.tagId=46 AND tagpro.tagId=40;

然而这条语句并没有执行成功,而是报错 Not unique table/alias: 'tagpro' ,意思是说两次INNER JOIN的表是同一个表/表别名,所以不行。所以我就试着把一个INNER JOIN删掉,然后再看是可以执行了,但是却是没有查到任何结果。到这里,我差点就要骂SQL不够智能了,明明是该产品在tagpro表中有tagId等于46也有tagId等于40,为什么你要理解成了 tagId同时等于46和40呢?

SELECT * FROM product p INNER JOIN tagpro ON tagpro.ProductId = p.ProductId WHERE onSale=1 AND tagpro.tagId=46 AND tagpro.tagId=40;

骂也没用,只能再想办法,相信这种事情很多人都遇到,SQL肯定有办法解决这种问题,后来尝试了一下这种写法,把同一个表分别用了两个不同的别名,然后终于能查出来了。 ≧▽≦

SELECT * FROM product p INNER JOIN tagpro a ON a.ProductId = p.ProductId INNER JOIN tagpro b ON b.ProductId = p.ProductId WHERE onSale=1 AND a.tagId=46 AND b.tagId=40;

六、产品控制器中的SQL查询函数

前面说了,Search控制器中的index()方法负责拼接SQL语句,提交到 Product控制器中进行产品的查询,现在在Product控制器中新建一个 getSearchPro() 方法,参考原来简单查询中的做法,另外加入JOIN的处理。这里其实就是把 where拼接起来, 把 join 拼接起来。原始的where和join的生成在Search控制器的index()中。

    /**根据筛选条件查找分类产品,多表查询     //默认每页16    //排序为销售阈值* @param string $sql 单表查询的SQL* @param int $countPerPage=16 每页商品数* @param string $orderBy='salseF DESC' 销量阈值* @param array $joinConfig=NULL 多表查询时 *         $joinConfig['joinTable']为联合表名二维数组,每一列遍历为 $joinTableL*                 $joinTableL[name]为真实表名  $joinTableL[asname]为as表名*         $joinConfig['where']为附加查询条件as表名的字段=条件  $joinTableL[asname].size=$size;* @return array $res['nowP']当前页数  $res['totalP']总产品数  $res['links']分页栏HTML $res['productList']产品二维数组*/    function getSearchPro($sql, $countPerPage = 16, $orderBy='salesF DESC', $joinConfig=NULL){        $productM = M('product')->alias('p');    // 实例化Data数据对象$where = $sql ? 'p.onSale=1 AND '.$sql : 'p.onSale=1';$joinTableArr = $joinConfig['joinTable'];    //要JOIN的表名$joinWhereArr = $joinConfig['where'];        //要筛选的格外条件foreach( $joinWhereArr as $joinLine ){$where .= ' AND '.$joinLine;}$tempSQL = $productM->distinct(true)->field('p.ProductId,p.name,p.mainPic,p.priceN,p.priceVIP,p.isNew,p.isHot,p.sales,p.salesF')->where( $where )->order( $orderBy );//处理JOINforeach( $joinTableArr as $joinTableL){$tempSQL = $tempSQL->join("$joinTableL[name] AS $joinTableL[asname] ON $joinTableL[asname].ProductId = p.ProductId");}$res = $this->executeTempSQL($tempSQL, $countPerPage, 'p.ProductId');return $res;}    

注意最后的 $res = $this->executeTempSQL($tempSQL, $countPerPage, 'p.ProductId'); 最后比之前的函数多了一个参数,因为前文提到的 executeTempSQL()方法中的 $count = $tempSQL->count();  改为了  $count = $tempSQL->count('DISTINCT '.$countCond); 否则在多表查询时计数会出现count的数量比实际查到的结果条数多的情况。 这里的executeTempSQL()后面新增的参数为 $countCond,默认值为'ProductId',以便单表查询时不必填写这个无相紧要的参数。

七、Search控制器,筛选项转换成SQL拼接

index()函数:生成查询的SQL语句段。逻辑是:
1、根据 get 的参数,分别依次进行筛选/排序处理;
2、只在product表中产生where条件的,以一次查询加 简单where SQL拼接的方式处理;
3、多表联合并在其它表有 where条件的,以 join 数组的形式提交给产品控制器统一拼接处理;
4、这个是目前现行的方案,以后还要再优化的;

    //搜索入口function index( $defaultTag=NULL ){        //如果改变了筛选条件,则去除页码参数$this->pageCheck();//********处理筛选**********************************$type2 = I('get.type2');        // type2: 篮球鞋、跑步鞋……$brand = I('get.brand');        // brand: 阿迪、匹克、李宁……$people = I('get.people');        // people: 男、女、中性……$size = I('get.size');            // size: 35~46、S~L……$price = I('get.price');        // price: 小于99、100~199……$tag = I('get.tag');            // tagsId: 限时、热销、性价比……$keyword = trim( I('get.keyword') );// keyword: 搜索关键字……$orderby = I('get.orderby');$joinTableNameIndex = 0;    //join所用的表替代名称后缀,为了两个表多次联合查询而设计所有的join表as tb0、tb1...//例如 SELECT * FROM product p INNER JOIN tagpro tb0 ON tb0.ProductId = p.ProductId INNER JOIN tagpro tb1 ON tb1.ProductId = p.ProductId WHERE onSale=1 AND tb0.tagId=46 AND tb1.tagId=40;$sql = '';//二级分类: /type2/篮球鞋if( $type2 ){$type2Id = M()->query("SELECT Id FROM sort WHERE sortName='$type2'");$type2Id = $type2Id[0]['Id'];$thesql = "p.sort_secType='$type2Id'";$sql = $sql ? $sql.' AND '.$thesql : $thesql;}//品牌筛选: /brand/李宁if( $brand ){$brandId = M()->query("SELECT Id FROM sort WHERE sortName='$brand'");$brandId = $brandId[0]['Id'];$thesql = "(p.sort_brand='$brandId' OR p.name LIKE '%$brand%')";    //这里除了品牌分类匹配外还根据商品名称模糊匹配$sql = $sql ? $sql.' AND '.$thesql : $thesql;}//性别: /people/男性 /people/女性 /people/中性if( $people ){//人群标签ID$tagsPeopleId = M('tags')->where("tag_name='$people'")->field('Id')->select();$tagsPeopleId = $tagsPeopleId[0]['Id'];$join['joinTable'][] = array('name'=>'tagpro', 'asname'=>'tb'.$joinTableNameIndex );$join['where'][] = 'tb'.$joinTableNameIndex.'.tagId='.$tagsPeopleId ;$joinTableNameIndex++;
//            $sql = $sql ? $sql.' AND '.$thesql : $thesql;
        }//营销标签(与人群标签一样处理逻辑)if( $tag ){$tagsId = M('tags')->where("tag_name='$tag'")->field('Id')->select();$tagsId = $tagsId[0]['Id'];$join['joinTable'][] = array('name'=>'tagpro', 'asname'=>'tb'.$joinTableNameIndex );$join['where'][] = 'tb'.$joinTableNameIndex.'.tagId='.$tagsId ;$joinTableNameIndex++;
//            $sql = $sql ? $sql.' AND '.$thesql : $thesql;
        }//尺码筛选: /size/35if( $size ){$join['joinTable'][] = array('name'=>'reserve', 'asname'=>'tb'.$joinTableNameIndex );$join['where'][] = 'tb'.$joinTableNameIndex.'.size='.$size ;$joinTableNameIndex++;
//            $join['joinTable'][] = 'reserve';
//            $join['where'][] = 'reserve.size='.$size ;
//            $sql = $sql ? $sql.' AND '.$thesql : $thesql;
        }//价格筛选: /price/100~199 /price/大于1999  /price/小于99if( $price ){
//            var_dump( $price );if( preg_match('/~/', $price) ){                //    echo '介于';$priceArr = explode('~', $price);$thesql = "p.priceVIP BETWEEN '$priceArr[0]' AND '$priceArr[1]'";                }elseif( preg_match('/大于/', $price) ){            //    echo '大于';$priceArr = explode('大于', $price);$thesql = "p.priceVIP>'$priceArr[1]'";    }elseif( preg_match('/小于/', $price) ){            //    echo '小于';$priceArr = explode('小于', $price);$thesql = "p.priceVIP<'$priceArr[1]'";}            $sql = $sql ? $sql.' AND '.$thesql : $thesql;}//搜索关键字if( $keyword ){$thesql = "(p.ProductId LIKE '%$keyword%' OR p.name LIKE '%$keyword%')";$sql = $sql ? $sql.' AND '.$thesql : $thesql;}//排序if( $orderby ){if( $orderby == 'default'){$orderbySQL = NULL;}elseif( $orderby == 'priceVIPa' ){$orderbySQL = 'priceVIP ASC';}elseif( $orderby == 'priceVIPd' ){$orderbySQL = 'priceVIP DESC';}else{$orderbySQL = $orderby.' DESC';}}//*******************************************    $productC = A('DataProduct');$res = $productC->getSearchPro($sql,20,$orderbySQL,$join);
//    var_dump($res);return $res;}

Search 控制器下的另外两个函数如下:

    /*** 如果改变了筛选条件,则去除页码参数,回到从第一页开始* 实现原理:如果存在p参数且不是最后一个参数时,则认为是修改了筛选条件* 这里有一点BUG,多项选择再翻页时、取消一个选项并不会回到第一页(因为p参数还是在最后)*/function pageCheck(){
//        var_dump( $_SERVER['HTTP_REFERER'] );
//        var_dump( $_GET );$getKeyArr = array_keys( $_GET ); //var_dump($getKeyArr);$pKeyIndex = array_search('p', $getKeyArr);
//        var_dump($pKeyIndex);$arrL = sizeof($getKeyArr); //var_dump($getKeyArr);if( $pKeyIndex!==FALSE && $pKeyIndex+1 < $arrL ){    //p参数如果不是在最后则为更改了筛选条件$cutPurl = $this->getCutURL('p');redirect( $cutPurl );}}/*** 获得切除了指定get值的URL* @param string $getKey 要去除的get键* @param string $CtrlName 控制器名,默认为页面URL中的控制器名* @return string 不含http://域名 的URL,可直接用于前端输出* */function getCutURL($getKey, $CtrlName=CONTROLLER_NAME){$getStr ='';$getArr = I('get.');unset($getArr[$getKey]);foreach( $getArr as $getKey=>$getVal){$getStr .= '/'.$getKey.'/'.$getVal;}
//        var_dump($getStr);        $thisURL = explode('.html', U("Index/search") );$thisURL = $thisURL[0];return $thisURL.$getStr;        }

pageCheck() 和 getCutURL()

转载于:https://www.cnblogs.com/batsing/p/4960781.html

产品列表页分类筛选、排序的算法实现(PHP)相关推荐

  1. VUE项目从详情页退回列表页,保留列表页的筛选条件(筛选条件为单选),以及刷新后恢复默认的一个筛选状态

    vue项目做的一个"花名册"页面,从"花名册"页面可以点击某一个人的名字,进入这个人的信息详情表格页面,而且这个人的信息详情页面中有一个"直属上级&q ...

  2. 美团项目 --- 产品列表页 8

    ❤ 项目源码 ❤ GitHub地址:https://github.com/Umbrella001/mtapp 文章目录 一.完成效果图 ① 面包屑的使用 ② 设计多级分类的DOM结构 ③ 产品卡片显示 ...

  3. PHP实现列表页综合筛选功能,dede织梦列表页联动筛选方法功能的实现

    [DEDECMS.织梦开源程序已经成为市场上主流仿站开源代码,它能够非常快速的放置一个已经建立好的网站,但是前提是这些网站比较简单,没有一些复杂的功能,那么使用DEDECMS仿站是最快速,最有效的.而 ...

  4. html产品列表页的设计,产品列表页.html

    丹青诺和 if (!(/msie [6|7|8|9]/i.test(navigator.userAgent))){ new WOW().init(); }; 全量收集,全效还田 丹青粪肥资源化利用模式 ...

  5. ECsoop 商品列表页属性筛选区品牌以LOGO形式显示

    1.修改 category.php 文件 将(大概215行) $sql = "SELECT b.brand_id, b.brand_name, COUNT(*) AS goods_num & ...

  6. layui列表筛选列_Shopify搜索产品并筛选产品列表功能介绍

    搜索产品并筛选产品列表 您的所有产品都列在 Shopify 的产品区域中.每个页面列出 50 种产品.要整理产品列表,并在跨多个页面的列表中查找产品,您可以对列表进行排序.搜索和筛选. 默认情况下,产 ...

  7. 织梦DedeCMS列表页多条件联动筛选功能(移动端列表页使用电脑端模板问题解决)

    原文:http://www.dedejs.com/html/article-571-1.html点击打开链接 备注:按此文可以解决,但手机列表页会出问题.列表页移动端调用的还是电脑端模板. 此问题本人 ...

  8. 美多商城项目:商品数据库表设计、准备商品数据、首页广告、商品列表页

    一.商品数据库表设计 1.1 SPU和SKU 在电商中对于商品,有两个重要的概念:SPU和SKU 1. SPU介绍 SPU = Standard Product Unit (标准产品单位) SPU是商 ...

  9. 美多商城之商品(商品列表页)

    四.商品列表页 4.1 商品列表页分析 4.1.1. 商品列表页组成结构分析 1.商品频道分类 已经提前封装在contents.utils.py文件中,直接调用即可. 2.面包屑导航 可以使用三级分类 ...

最新文章

  1. centos下axel安装与使用
  2. Office OpenXML-Excel(一)
  3. KMP--字符串匹配
  4. kali设置中文_kali安装教程
  5. 下拉框的value值怎么设置为变量_自绘制HT For Web ComboBox下拉框组件
  6. ECCV 2020 论文大盘点-人脸技术篇
  7. 语义信息增强的激光雷达SLAM
  8. springcloud使用zipkin和rabbitmq进行服务链路追踪
  9. kali linux虚拟机密码,[Linux]安装kali虚拟机后忘记root密码
  10. CISCO、JUNIPER网络技术训练中心ITAA等级四(RS)学习计划蓝图(version 1.0)
  11. HDU 6034 - Balala Power! | 2017 Multi-University Training Contest 1
  12. uniapp使用cmd引入uview报错,文件查找失败:‘uview-ui‘
  13. Bootstrap基本使用
  14. 支付接口被阻拦,支付宝提示ILLEGAL_EXTERFACE
  15. Html代码问题:用鼠标悬停在文字上文字的颜色就变颜色的效果
  16. 芝麻信用分有哪些计算维度?关于大数据风控的87个问题
  17. 加速ubuntu开机速度
  18. 数学建模常用算法:启发式优化算法合辑(内含多种智能优化算法,使用java实现算法、详细注释、并进行结果可视化)
  19. 6.见过最强的后台管理系统
  20. css层叠样式表——css基础介绍

热门文章

  1. 异常检测: 多元高斯分布
  2. 跨站点脚本编制-XSS 描述及解决方法
  3. 如何列出引用SQL Server中给定表的所有外键?
  4. 在C#中,List struct中的值是否装箱?
  5. php access类,一个简洁的PHP操作Access类
  6. win11快捷键失效怎么处理 Windows快捷键失效的解决方法
  7. Webots安装,及urdf转webots,通过3d模型获取Proto_indexedFaceset数据,MoveIt机械臂路径规划
  8. web重启tomcat_使用docker构建jdk镜像和web服务镜像
  9. cfiledialog 保存 扩展名_吃不完的红薯很难保存?教你一招,放1年不烂不发芽,简单实用...
  10. java httpclient 进度条_SpringBoot如何实现一个实时更新的进度条的示例代码