Laravel Eloquent 查询使用 Case/When 动态应用条件

问题描述 投票:0回答:1

我正在尝试根据机器人表的 start_date 和 end_date 列的值应用一组不同的日期过滤器

状况

  1. 如果机器人开始日期&&机器人结束日期均为空=>返回所有数据
  2. 否则如果机器人的开始日期为NULL且结束日期不为NULL,则返回computer_vision_detections的created_at日期小于或等于机器人的end_date的所有数据(仅比较日期,忽略时间)。

问题

查询的

WHEN
部分的应用方式存在问题,尽管 start_date 和 end_date 列都是
NULL
,但它仍然进入第二个
WHEN
查询,导致在应该有数据时没有返回数据返回 500 条数据

代码

    $robot = App\Robot::with([
        'computer_vision_detections' => function ($query) {
            $query->leftJoin('cv_detection_object_values', function ($join) {
                $join->on('computer_vision_detections.detection_object_id', '=', 'cv_detection_object_values.id')
                     ->whereColumn('computer_vision_detections.detection_type_id', '=', 'cv_detection_object_values.detection_type_id');
            })
            ->leftJoin('robots', 'computer_vision_detections.serial_number', '=', 'robots.serial_number')
            ->select(
                'computer_vision_detections.*',
                'cv_detection_object_values.detection_type_id AS cv_detection_object_values_detection_type_id',
                'cv_detection_object_values.id AS cv_detection_object_values_id_detection_object_id',
                'cv_detection_object_values.description AS cv_detection_object_values_description'
            )
            ->where(function ($query) {
                $query->when(
                    DB::raw('robots.start_date IS NULL AND robots.end_date IS NULL'),
                    function ($q) {
                        $q->whereNotNull('computer_vision_detections.id'); // Take all data if both start and end are null
                    }
                )
                ->when(
                    DB::raw('robots.start_date IS NULL AND robots.end_date IS NOT NULL'),
                    function ($q) {
                        $q->whereRaw('DATE(computer_vision_detections.created_at) <= DATE(robots.end_date)'); // All data before or equals to end date
                    }
                );
            });
        },
        'computer_vision_detections.detection_type' // This assumes the relationship is defined properly
    ])
    ->find(9434);

调试

WHEN
查询只是简化为这一个条件时,它能够返回 500 个数据条目,这向我表明查询的这一部分没有问题,并且问题是由于第二个
WHEN尽管 end_time 为 
NULL
,但查询仍在执行,因此导致返回的条目为 0

->where(function ($query) { $query->when( DB::raw('robots.start_date IS NULL AND robots.end_date IS NULL'), function ($q) { $q->whereNotNull('computer_vision_detections.id'); // Take all data if both start and end are null } ); });
    
mysql laravel eloquent case eloquent-relationship
1个回答
0
投票
如果您查看文档中的[条件子句][1],您将看到以下语句:

有时您可能希望将某些查询子句应用于基于另一个条件的查询。

这个例子:

$role = $request->input('role'); $users = DB::table('users') ->when($role, function (Builder $query, string $role) { $query->where('role_id', $role); }) ->get();
这里有第一个参数,称为 

$role

,如果它为 true,则 
where
 将添加到查询中。

但是,您对

when

 的期望是根据查询的部分结果有条件地添加查询部分。然而,事实并非如此。首先,您编写(或在本例中生成)查询,然后执行它,只有这样您才会知道结果是什么。在构造查询时,您还没有查询的结果,因此 
robots.start_date IS NULL AND robots.end_date IS NOT NULL
 不会被有意义地评估,而您得到的是 
DB::raw('some text here')
 是否为 truey。

DB::raw

 返回一个 
\Illuminate\Database\Query\Expression
,如果不是 
null
,则始终为 true,并且它不会与您想要的检查有意义地等效。

您可以采用以下方法来代替当前的方法:

->whereRaw(' CASE WHEN robots.start_date IS NULL AND robots.end_date IS NOT NULL THEN computer_vision_detections.id IS NOT NULL WHEN robots.start_date IS NULL AND robots.end_date IS NOT NULL THEN DATE(computer_vision_detections.created_at) <= DATE(robots.end_date) ELSE TRUE END ')
这将您的第一个案例封装在第一个

WHEN

中,因此如果满足,则检查另一个字段是否为
null
,第二个案例与您的日期比较,如果两者都不是,则返回到
TRUE
是正确的。
[1]:
https://laravel.com/docs/11.x/queries#conditional-clauses

© www.soinside.com 2019 - 2024. All rights reserved.