You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中DB查询构建器无has/doesNotHave方法,如何替代?

解决Laravel DB查询构建器中has()/doesNotHave()方法报错的问题

问题原因

has()和doesNotHave()是Eloquent ORM专属方法,仅能在Eloquent模型实例上调用。你当前使用的DB::table()返回的是Illuminate\Database\Query\Builder实例(原生查询构建器),该类并没有这两个方法,因此会抛出Call to undefined method错误。

解决方案

根据场景选择以下两种方案之一:

方案1:改用Eloquent模型(推荐)

如果你已经定义了Order模型,且在模型中配置了customer和logistics关联(比如belongsTo/hasOne),可以直接用Eloquent重构查询,自然支持has()和doesNotHave():

use Carbon\Carbon;
use App\Models\Order;

$startDate = Carbon::today()->startOfDay();
$endDate = Carbon::today()->endOfDay();

if (request('start_date') && request('end_date')) {
    $startDate = Carbon::parse(request('start_date'))->startOfDay();
    $endDate = Carbon::parse(request('end_date'))->endOfDay();
} elseif (request('current_date')) {
    $startDate = Carbon::parse(request('current_date'))->startOfDay();
    $endDate = Carbon::parse(request('current_date'))->endOfDay();
}

$orders = Order::has('customer')
    ->doesntHave('logistics')
    ->where('should_deliver', 'now')
    ->whereIn('status', ['completed', 'approved', 'success'])
    ->where('stocked', false)
    ->whereBetween('created_at', [$startDate, $endDate])
    ->selectRaw("DATE_FORMAT(created_at, '%Y-%m') as month")
    ->selectRaw('COUNT(*) as total_orders')
    ->selectRaw("SUM(CASE WHEN delivery_statuses.status != 'confirmed' THEN 1 ELSE 0 END) as total_unconfirmed")
    ->selectRaw("SUM(CASE WHEN delivery_statuses.status = 'confirmed' THEN 1 ELSE 0 END) as total_confirmed")
    ->selectRaw('MIN(created_at) as min_created_at')
    ->leftJoin('delivery_statuses', 'orders.id', '=', 'delivery_statuses.order_id')
    ->groupBy('month')
    ->orderBy('min_created_at', 'desc')
    ->get();

方案2:用原生SQL条件替代(适配原生查询构建器)

若必须使用DB::table(),可以用WHERE EXISTS和WHERE NOT EXISTS子句替代has()和doesntHave()的逻辑,需要将关联检查整合到主查询的条件中:

use Carbon\Carbon;

$startDate = Carbon::today()->startOfDay();
$endDate = Carbon::today()->endOfDay();

if (request('start_date') && request('end_date')) {
    $startDate = Carbon::parse(request('start_date'))->startOfDay();
    $endDate = Carbon::parse(request('end_date'))->endOfDay();
} elseif (request('current_date')) {
    $startDate = Carbon::parse(request('current_date'))->startOfDay();
    $endDate = Carbon::parse(request('current_date'))->endOfDay();
}

$orders = DB::table(DB::raw("
    (SELECT
        DATE_FORMAT(orders.created_at, '%Y-%m') as month,
        COUNT(*) as total_orders,
        SUM(CASE WHEN delivery_statuses.status != 'confirmed' THEN 1 ELSE 0 END) as total_unconfirmed,
        SUM(CASE WHEN delivery_statuses.status = 'confirmed' THEN 1 ELSE 0 END) as total_confirmed,
        MIN(orders.created_at) as min_created_at
    FROM orders
    LEFT JOIN delivery_statuses ON orders.id = delivery_statuses.order_id
    WHERE orders.should_deliver = 'now'
    AND orders.status IN ('completed', 'approved', 'success')
    AND orders.stocked = false
    AND orders.created_at BETWEEN ? AND ?
    -- 替代has('customer'):检查订单关联的客户存在
    AND EXISTS (SELECT 1 FROM customers WHERE customers.id = orders.customer_id)
    -- 替代doesntHave('logistics'):检查订单无关联的物流记录
    AND NOT EXISTS (SELECT 1 FROM logistics WHERE logistics.order_id = orders.id)
    GROUP BY month
    ) as subquery
"))
->addBinding($startDate)
->addBinding($endDate)
->orderBy('subquery.min_created_at', 'desc')
->get();

注意:请根据你的实际表结构调整customers和logistics表的关联字段(比如orders.customer_id是订单关联客户的外键,需和你的数据库一致)。

内容的提问来源于stack exchange,提问作者Leslie Joe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 09:34:51