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
相关产品推荐
相关产品推荐

