Laravel 系列:orWhere 条件式

Laravel,一个优雅的 PHP 框架,也是目前最火的 PHP 项目,其生态积极健康,社区也很活跃。:sparkling_heart:

目前我对于学习 Laravel 还是有些吃力的,基本方法都能很快掌握,但是一旦遇到了比较复杂的问题后,想要去查看源码进行调试的时候,总是会显得不知所措。:dizzy_face: :dizzy_face: :dizzy_face: 官方文档不会把所有方法都讲一遍,也不会把方法的各个使用场景都描述一遍,所以遇到这些问题去看文档一般解决不了。

改正一下,针对我下面这个问题,其实文档中有,是自己没有看仔细,不好意思啦:传送门

这次我遇到的问题是 orWhere 条件式造成的查询语句写法始终不是自己想要的。

场景描述

我需要根据商品分类、商品名称和条形码这三个查询条件去取出结果集,这三者的关系是:商品分类 and (商品名称 or 条形码)

错误尝试

第一次写出来的代码是这样的:

$goodsModel = new Goods;

if (!empty($category)) {
    $goodsModel = $goodsModel->where('category', $category);
}

if (!empty($keyword)) {
    $goodsModel = $goodsModel->where('name', 'like', "%{$keyword}%")->orWhere('barcode', 'like', "%{$keyword}%");
}

$goodsList = $goodsModel->limit(10)->get();

然后会发现这样的写法会导致三者的关系变成了:商品分类 and 商品名称 or 条形码。:shit: :shit: :shit:

谷歌一下

也是找了一会才找到,如果需要将 or 两边的查询项作为一组查询条件,那么建议使用 where 的闭包方式。

闭包

这种方式将 or 部分写成 closure 传入 where(),与 Eloquent 风格一致,可读性较好。代码如下:

if (!empty($keyword)) {
    $goodsModel = $goodsModel->where(function ($query) use ($keyword) {
        $query->where('name', 'like', "%{$keyword}%")->orWhere('barcode', 'like', "%{$keyword}%");
    });
}

原生

实在想不出怎么写,就写原生 SQL 好了。这也是最后一种保险但不提倡的做法了吧,代码如下:

if (!empty($keyword)) {
    $keyword = "%{$keyword}%";
    $goodsModel = $goodsModel->whereRaw('(name like ? or barcode like ?)', [$keyword, $keyword]);
}

需要注意的是,单引号里面的括号是不能省略的,否则一样会导致这三者的关系是:商品分类 and 商品名称 or 条形码。

占位符

再友情提示一下,你可能会对 like ? 觉得奇怪,我第一次写的时候直接是 whereRaw('name like "%?%"', [$keyword]'),但是这样是没效果的,原因是占位符是对某一个完整的值进行占位,而不是简单的替换效果,所以需要将 like 的参数全部用 ? 替代,将百分号的拼接放在后面的占位数组的变量中去。:hear_no_evil: :hear_no_evil: :hear_no_evil:

参考链接:How do you parameterize whereRaw() in the query builder?

总结

一门技术,一个框架,想要精通,需要学的和看的有很多很多呢。。。:triumph: :triumph: :triumph:

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

我手头没有电脑,你试试下面的写法是否可行。

DB::table('users')
            ->where('name', '=', 'John')
            ->where(function ($query) {
                $query->where('votes', '>', 100)
                      ->orWhere('title', '<>', 'Admin');
            })
            ->get();

我没试过,但理论上是可以的。另外,你也可以将判断条件拆分成 or 连接的形式,然后用类似

$users = DB::table('users')->where([
    ['status', '=', 1],
    ['subscribed', '<>', 1]
])
->orWhere(…)
->get();

这样的方式查找,我看见你整个逻辑执行了两条 SQL,其实是可以减少到一条的。

6年前 评论
wenqing

写的不错,以前都是直接写原生了。

6年前 评论

我手头没有电脑,你试试下面的写法是否可行。

DB::table('users')
            ->where('name', '=', 'John')
            ->where(function ($query) {
                $query->where('votes', '>', 100)
                      ->orWhere('title', '<>', 'Admin');
            })
            ->get();

我没试过,但理论上是可以的。另外,你也可以将判断条件拆分成 or 连接的形式,然后用类似

$users = DB::table('users')->where([
    ['status', '=', 1],
    ['subscribed', '<>', 1]
])
->orWhere(…)
->get();

这样的方式查找,我看见你整个逻辑执行了两条 SQL,其实是可以减少到一条的。

6年前 评论
imxfly

@zhangbao 可能你没有看仔细哦,我就写了一条SQL,只不过将链式调用拆成了两个,最终查询还是就一个的呢。简化一下就像这样:

$goodsModel = new Goods;
$goodsModel = $goodsModel->where('name', 'like', '%mike%');
$goodsModel = $goodsModel->where('age', 22);
$goodsModel->get();

至于为什么这么写的原因,因为我有多个查询条件,但是如果其中的某些条件未选或者选的是默认值,就不需要写入查询语句中去了(咳咳,不知道我讲清楚我这个意图了么 :hear_no_evil: )。

6年前 评论
imxfly

@wenqing 有时候用用还是很爽的,比用封装的API写反而感觉要清爽(怎么肥事???:runner: )

6年前 评论

这种情况我遇到过了,不过我用的是闭包。laravel 有when 这个方法,其实也可以解决

$role = $request->input('role');

$users = DB::table('users')
                ->when($role, function ($query) use ($role) {
                    return $query->where('role_id', $role);
                })
                ->get();

当$role 有值得时候才去执行,文档https://learnku.com/docs/laravel/5.5/queries#where-clauses,不知道我说的对不对

6年前 评论
imxfly

@tanjibo 这个可以,但如果第一个参数我是经过比较复杂的判断得出是否是true or false,那感觉用 when 代码就有点难受了 :speak_no_evil:

6年前 评论
godruoyi

我刚刚给你的公众号点赞了!!!

6年前 评论
imxfly

@godruoyi 谢谢捧场,扎心了老铁 :hear_no_evil:

6年前 评论

$keyword 为 _% 需要处理下

6年前 评论

whereIn可以加or吗

5年前 评论

刚好遇到这个问题,看了分享已解决,感谢!

5年前 评论

挺清晰的!有条理 !没多余的东西 加油

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!