SQL 的括号怎麽写成 Laravel Query?

前言

之前工作遇到一个情境需要捞出取消订单状态为 0,1 或没有取消单的订单,然後要再加上其他条件,像是订单状态、时间和供应商名称。

SQL 大概是长这样,OR的条件用括号刮起来

SELECT * FROM orders
WHERE status = 1
AND (/* 括号 */
    EXISTS (
        /* 取消订单状态为 0 或 1 */
        SELECT * FROM cancellations 
        WHERE orders.id = cancellations.order_id
        AND cancellations.status in (0, 1)
    ) OR NOT EXISTS (
        /* 或没有取消订单 */
        SELECT * FROM cancellations 
        WHERE orders.id = cancellations.order_id
    )
)/* 括号 */ 
AND DATE (created_at) >= "2021-07-01 00:00:00"
AND DATE (created_at) <= "2021-07-31 23:59:59"
AND supplier = "appple";

所以写成 Laravel query 长这样

Order::where('status', 1)
whereHas('cancellation', function($query) {
    return $query->whereIn('status', [0, 1]);
})
->orWhereDoesntHave('cancellation')
->where('created_at', '>=', '2021-07-01 00:00:00')
->where('created_at', '<=', '2021-07-31 23:59:59')
->where('supplier', 'apple');

问题来了!我要怎麽加那个「括号」呢!?

各种尝试

爬了很多文和尝试,得出的结论是,以这个 case ,最简单的方式就是不加!只是把 or 条件移到最後,像这样。结果会是我要的。

Order::where('status', 1)
->where('created_at', '>=', '2021-07-01 00:00:00')
->where('created_at', '<=', '2021-07-31 23:59:59')
->where('supplier', 'apple')
/* 下面这段放最後面 */
whereHas('cancellation', function($query) {
    return $query->whereIn('status', [0, 1]);
})
->orWhereDoesntHave('cancellation');

但这样的缺点是可能比较难维护,例如之後的人要再加一个条件,他不知道这个情形,就直接在最後面接上一个新条件就 GG 了!而且如果 OR 的条件不只一个,这个写法也不行。

後来我查了 Laravel query ExistsNot Exists 的写法,拼出了以下的写法,结果也是对的!只是比较丑...

逻辑基本上就是想办法把 SQL 用 Laravel query 的语法硬写出来。

Order::where('status', 1)
whereExists(function($query) {
    return $query->from('cancellations')
    ->whereColumn('orders.id', 'cancellations.order_id')
    ->whereIn('cancellations.status', [0, 1])
    ->orWhereNotExists(function ($query){
      return $query->from('cancellations')
      ->whereColumn('orders.id', 'cancellations.order_id');
    });
})
->where('created_at', '>=', '2021-07-01 00:00:00')
->where('created_at', '<=', '2021-07-31 23:59:59')
->where('supplier', 'apple');

成功是成功了,但我还是不是很满意,觉得这样的写法很不优雅。後来跟同事讨论才知道原来可以把 where 当括号来用!知道 Laravel query 的括号怎麽写後就单纯很多了!

Order::where('status', 1)
->where(function ($query) {/* 括号! */
    return $query->whereHas('cancellation', function($query) {
        return $query->whereIn('status', [0, 1]);
    })->orWhereDoesntHave('cancellation');
})/* 括号! */
->where('created_at', '>=', '2021-07-01 00:00:00')
->where('created_at', '<=', '2021-07-31 23:59:59')
->where('supplier', 'apple');

所以就算之後里面还要多个括号就再加 where 就好~满足~

最後补充

如果条件是变数,可以用 use 把变数传进 where

$status = [0, 1]; /* 状态变数 */

Order::where('status', 1)
->where(function ($query) use ($status) {/* 传 $status 进去 */
    return $query->whereHas('cancellation', function($query) {
        return $query->whereIn('status', $status);/* 用在这 */
    })->orWhereDoesntHave('cancellation');
})
->where('created_at', '>=', '2021-07-01 00:00:00')
->where('created_at', '<=', '2021-07-31 23:59:59')
->where('supplier', 'apple');

<<:  Day 01 初见Blazor

>>:  [很不铁人的 IT 铁人赛] 前端工程师学资讯安全

ASP.NET MVC 从入门到放弃(Day16)-MVC基本概念

总算要进入MVC架构介绍了... 前面整整15天都在讲基础的部分,简易说明资料库如何连线、C# 基本...

[Android Studio 30天自我挑战] ListView 元件介绍

当遇到大量且有规律的资料就可以用ListView清单显示,例如:商品讯息,联络人... ListVi...

求职 DBA 资料库管理师的准备方向 - 心得分享

DBABootcamp 如果你正在思考未来的职涯规划,准备投入或转换 DBA (资料库管理师) 跑道...

[Golang]变数重声明与重名变数

一、整理变数重声明与重名变数的描述。 变数重声明,对已经声明过的变数,再次声明。 前提条件如下: 变...

[Golang]同步工具-sync包的原子操作(上)-心智图总结

1. 原子操作是什麽? 原子操作,操作进行的过程不能被中断。也就是说,某个值在原子操作的过程中,CP...