Laravel 框架中 一个搜索框搜多个字段解决方案优化(whereRaw like 参数绑定)

Laravel 框架中 一个搜索框搜多个字段解决方案优化(whereRaw like 参数绑定)

1.缘由

业务需要一个输入框框搜多个字段引起

  • name,phone,status 为示例字段

一般情况下,下方写法可满足要求(谢谢下方评论提示,一开始我也是这样写的)
demo1:

$list = $list->where(function($query)use($keyword){
       $query->where('name', 'like', '%'.$keyword.'%');
       $query->orWhere('phone', 'like', '%'.$keyword.'%');
});

但我们的需求中,存储为int数值的字段,在模糊查询时,也要查出来

  • 例:状态status字段,数据库中存储为tinyint类型,值为1/2等,表示为‘开始/关闭’,在输入‘关闭’后,也可以查出来

故调整为下方写法
demo2:

$list->whereRaw("concat(name,phone, (CASE
            WHEN status = ".Model::is_open_no." THEN
            '关闭'
            WHEN status = ".Model::is_open_yes." THEN
            '开启'
            END),) like '%".$keywords."%'");
  • 为提高文章的可读性,CASE 部分代码在下方demo中不再出现

2.问题

使用demo2实现,没有对sql注入做任何处理,仅输入 like ‘ 或 ‘ 就会报错
,要使用类似于 PDO 参数绑定进行传参,以避免 SQL 注入的风险

3.解决

Laravel 中 whereRaw 方法默认传参为1个参数,查看底层实现如下

    public function whereRaw($sql, $bindings = [], $boolean = 'and')
    {
        $this->wheres[] = ['type' => 'raw', 'sql' => $sql, 'boolean' => $boolean];

        $this->addBinding((array) $bindings, 'where');

        return $this;
    }

第二参数为要绑定的参数。同时在社区wiki中,summer大佬指出要避免sql注入和第二参数的使用方式

3.1 遇到的问题一:参数绑定不生效

对于like模糊查询,按社区wiki中写的如下代码,不生效(打印查看sql应该是没解析成功):
失败demo1:

$list = $list->whereRaw("concat(name,phone) like '%?%'",[$keywords]);

在查看PHP PDO之MySQL参数绑定后,实现如下:
成功demo1:

$list = $list->whereRaw("concat(name,phone) like ?",["%$keywords%"]);

3.2 遇到的问题二:数据中的某个字段为null时搜索不生效

按demo1实现,在测试过程中发现:如果遇到一条数据某个字段为null的情况下,搜该条数据其他字段,会出现搜不到的情况
示例:[name=’王三’,phone=’’],输入‘王三’时搜不到该条数据
处理:判断字段是否为 null,实现如下:
最终demo2:

$list = $list->whereRaw("concat(IFNULL(name,''),IFNULL(phone,'')) like ?",["%$keywords%"]);

4.总结

还是自己对基础掌握不牢,虽然之前都学过,但慢慢的不用也就忘了。
从使用框架的东西后,一些框架封装好的东西直接上手用,但底层的实现都没注意过

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 5

盲猜字符的符号laravel自己给你添加了,所以你手动写‘%?%’就等于是‘%'xxxxxx'%’

1周前 评论

假如某条记录姓名为 null,搜索手机号会找不到这条记录吧。

1周前 评论
请多多指教 (楼主) 1周前

为什么不这样呢

$list = $list->where(function($query)use($keyword){
       $query->where('name', 'like', '%'.$keyword.'%');
       $query->orWhere('phone', 'like', '%'.$keyword.'%');
});
1周前 评论
zpers 1周前
请多多指教 (楼主) 1周前
请多多指教 (楼主) 1周前

我觉得做的复杂了。

Model::where(function($query){
      $value = trim(request('search_name',''));
      $query->where('name','like',"%$value%")
                 ->orWhere('phone','like',"%$value%")
                 ->orWhere(function($query)use($value){
                      $map = collect([
                          '关闭' => 1,
                          '开启' => 2,
                      ]);
                      if(is_int((real)$value)){
                          $query->where('status',$value);
                      }elseif($map->has($value)){
                          $query->where('status',$map->get($value);
                      }else{
                          $query->where('status','like',"$value")
                      }                     
        })
    })->latest()->paginate(request('size',10));

这里仅供参考毕竟伪代码,直接使用mysql函数感觉太复杂了。name,phone 应该是string 类型的列所以应该不能为null,默认值设置为empty string,就不会出现null 使用like查不出来。另外,你那个sql 的弊端是,如果用户输入1,2 你原生的status字段存在的值你反而查不出来。

1周前 评论
zpers 1周前

这业务看起来八成是后台管理系统的搜索业务,多字段查询一个值我觉得非常不好,意义不大,有时候我只想查139开头的手机号,你把用户名称包含139的都搜索出来了,数据量大的时候翻几页你都找不到139开头的手机号。毕竟没有权重的概念,第二就是查询效率问题。

1周前 评论
请多多指教 (楼主) 1周前

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!