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

