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

使用DB::select()原生查询时,如何运用Laravel关联关系?

嘿,这个问题挺常见的——当你用DB::select()写原生SQL时,确实没法直接用Eloquent的关联关系,因为它返回的是普通PHP对象,不是模型实例。不过有几种办法能让你兼顾原生查询的灵活性和Eloquent关联的便捷性,咱们一步步来看:

1. 把原生查询结果转为Eloquent模型

如果你已经为guarantee_tickets和guarantee_tickets_answers定义了Eloquent模型,你可以把DB::select()的结果转换成模型实例,这样就能调用模型里预定义的关联方法了。

另外先敲个重点:你的原代码存在严重的SQL注入风险,直接把$request->id拼进SQL里绝对不可取,一定要用参数绑定!

示例代码如下:

// 安全执行原生查询,用参数绑定替代直接拼接变量
$rawResults = DB::select("
    (select ga.id, '' as unique_product_id, ga.user_id, ga.submit_time, '' as title, ga.description, '' as tracking_code, '' as closed, '' as close_date, ga.isAdminNote 
     from guarantee_tickets_answers ga 
     where guarantee_tickets_id = ? 
     order by ga.id desc) 
    union all 
    (select gt.id, gt.unique_product_id, gt.user_id, gt.submit_time, gt.title, gt.description, gt.tracking_code, gt.closed, gt.close_date, '' 
     from guarantee_tickets gt 
     where id = ?)
", [$request->id, $request->id]);

// 将原生查询结果转为对应模型实例
$guaranteeTickets = collect($rawResults)->map(function($item) {
    // 根据字段区分记录类型,分别转换为对应模型
    if (!empty($item->title)) {
        return new App\Models\GuaranteeTicket((array)$item);
    } else {
        return new App\Models\GuaranteeTicketAnswer((array)$item);
    }
});

// 现在就能直接调用模型关联了
foreach ($guaranteeTickets as $ticket) {
    if ($ticket instanceof App\Models\GuaranteeTicket) {
        $user = $ticket->user; // 假设GuaranteeTicket模型已定义user关联
    } elseif ($ticket instanceof App\Models\GuaranteeTicketAnswer) {
        $parentTicket = $ticket->guaranteeTicket; // 假设Answer模型已定义关联到父ticket的关系
    }
}

2. 改用Eloquent查询构造器实现Union查询

其实Laravel的查询构造器本身支持Union操作,用这种方式写出的查询天然返回Eloquent模型实例,直接就能用关联关系,而且更安全、更符合Laravel的最佳实践。

你可以把原查询重构为:

// 构建第一个子查询(来自guarantee_tickets_answers)
$answersQuery = App\Models\GuaranteeTicketAnswer::select(
    'id',
    DB::raw("'' as unique_product_id"),
    'user_id',
    'submit_time',
    DB::raw("'' as title"),
    'description',
    DB::raw("'' as tracking_code"),
    DB::raw("'' as closed"),
    DB::raw("'' as close_date"),
    'isAdminNote'
)->where('guarantee_tickets_id', $request->id)->orderBy('id', 'desc');

// 构建第二个子查询(来自guarantee_tickets)
$ticketsQuery = App\Models\GuaranteeTicket::select(
    'id',
    'unique_product_id',
    'user_id',
    'submit_time',
    'title',
    'description',
    'tracking_code',
    'closed',
    'close_date',
    DB::raw("'' as isAdminNote")
)->where('id', $request->id);

// 执行Union查询并获取模型集合
$guaranteeTickets = $ticketsQuery->union($answersQuery)->get();

// 直接调用关联关系即可
foreach ($guaranteeTickets as $ticket) {
    $relatedUser = $ticket->user; // 直接使用模型中定义的关联
}

这种方式的优势很明显:

  • 自动处理参数绑定,彻底避免SQL注入
  • 返回的是完整的Eloquent模型实例,无需额外转换
  • 代码可读性、可维护性更强,符合Laravel的生态规范

总结

优先推荐用查询构造器实现Union查询,这是最省心的方案;如果因为特殊需求必须用原生SQL,一定要记得做参数绑定,再把结果转为模型实例来使用关联关系。

内容的提问来源于stack exchange,提问作者Martin AJ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:49:50