Laravel Eloquent Postgresql Select with Case,然后按 Case 结果过滤

Posted

技术标签:

【中文标题】Laravel Eloquent Postgresql Select with Case,然后按 Case 结果过滤【英文标题】:Laravel Eloquent Postgresql Select with Case and then filter by Case result 【发布时间】:2021-04-02 15:52:11 【问题描述】:

我有一个 CASE 原始语句,它为每一行生成一个状态。如果用户不想查看所有项目但特定集合,我想按此状态进行过滤。

$query = Device::leftJoin('devices_meters', 'devices_meters.device_id', '=', 'devices.id')
    ->leftJoin('device_measurements', 'device_measurements.device_id', '=', 'devices.id')
    ->select(
        'devices.*',
        DB::raw("
            CASE
                WHEN
                    devices.retired = false
                    AND devices.last_reported_utc_at > :dateFilter
                    AND SUM(device_measurements.flow) > 0
                THEN 'active'
                WHEN
                    devices.retired = false
                    AND devices.last_reported_utc_at > :dateFilter
                    AND SUM(device_measurements.flow) = 0
                THEN 'online'
                WHEN
                    devices.retired = false
                    AND devices.last_reported_utc_at <= :dateFilter
                THEN 'offline'
                WHEN
                    devices.retired = false
                    AND devices.serial_number IS NULL
                THEN 'inactive'
                WHEN
                    devices.retired = true
                THEN 'retired'
            END AS status,
            MAX(devices_meters.activated_at) AS activated_at,
            MAX(devices_meters.created_at) AS latest
        "),
    )
    ->setBindings(['dateFilter' => $dateFilter]);
    ->groupBy('devices.id', 'devices_meters.activated_at')
    ->orderBy('devices.installed_at', 'DESC')
    ->paginate($user->settings_pagination);

我试过了:

$query->where('status', '=', $status);
$query->having('status', '=', $status);

但是这些不起作用。我可以通过status 列过滤的方式是什么?

【问题讨论】:

【参考方案1】:

由于status 并不是真正的列,所以只需要使用整个case 语句进行过滤即可。

如果$status 是“活动的”,那么您需要添加

WHERE (
    devices.retired = false
    AND devices.last_reported_utc_at > :dateFilter
    AND SUM(device_measurements.flow) > 0
)

每个状态的 SQL 查询等等。

但是,使用 Eloquent,您可以 define local query scopes in your Device model

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class Device extends Model

    public function scopeActive(Builder $query, $dateFilter)
    
        $hasNecessaryJoins = collect($query->getQuery()->joins)
                             ->contains('table', 'device_measurements');

        return $query
            ->unless($hasNecessaryJoins, function ($subquery) 
                // do the left Join if it's not done already
                $subquery->leftJoin('device_measurements', 'device_measurements.device_id', '=', 'devices.id'));
            )
            ->where(function($subquery) use ($dateFilter) 
                $subquery->where('retired', false)
                         ->where('last_reported_utc_at', '>', $dateFilter)
                         ->whereRaw('SUM(device_measurements.flow) > 0');
            );
    

    public function scopeOnline(Builder $query, $dateFilter)
    
        // online seems to be the same as active?
        return $query
            ->active($dateFilter);           
    

    public function scopeOffline(Builder $query, $dateFilter)
    
        return $query
            ->where(function($subquery) use ($dateFilter) 
                $subquery->where('retired', false)
                         ->where('last_reported_utc_at', '<=', $dateFilter);
            );
    

    public function scopeInactive(Builder $query, $dateFilter)
    
        return $query
            ->where(function($subquery) 
                $subquery->where('retired', false)
                         ->whereNull('serial_number');
            );
    

    public function scopeRetired(Builder $query, $dateFilter)
    
        return $query
            ->where('retired', true);
    

有了这些范围,您可以致电$query-&gt;active($dateFilter)$query-&gt;online($dateFilter)$query-&gt;offline($dateFilter)$query-&gt;inactive($dateFilter)$query-&gt;retired($dateFilter)

所以如果你有$status = 'active',你可以打电话给$query-&gt;$status($dateFilter)

【讨论】:

以上是关于Laravel Eloquent Postgresql Select with Case,然后按 Case 结果过滤的主要内容,如果未能解决你的问题,请参考以下文章

Laravel Eloquent 多对多,增量依赖

Laravel 5.3 Eloquent 交易和外键限制

在 Laravel 中使用诸如 JSON 函数之类的 Postgres 功能

php Laravel 5 Eloquent CheatSheet #laravel #eloquent

php Laravel 5 Eloquent CheatSheet #laravel #eloquent

php Laravel 5 Eloquent CheatSheet #laravel #eloquent