Laravel多态关联查询时如何获取关联模型的name、email等指定字段
问题原因
- 你尝试在
select中写customer.name报错是完全合理的:with预加载机制是分两步执行SQL,第一步先查询orders主表数据,第二步根据主表返回的customer_type、customer_id字段查询对应的关联表,最后把关联结果拼装到模型属性中。主表查询和关联查询是独立执行的,主表查询时根本没有加载关联表,自然找不到customer.name字段。 - 你当前返回的
customer为null,是因为morphTo关联必须依赖主表的customer_type(存储关联模型类名)和customer_id(存储关联模型主键)两个字段做匹配,你在select中没有查询这两个字段,预加载时无法匹配到对应关联数据。
解决方案
方案1:预加载模式(推荐,写法最简单)
先把morphTo必须的两个字段加入主表查询,同时可以在预加载时指定只需要的关联字段减少查询开销:
$result = Order::query() ->with([ 'customer' => function ($query) { // 关联查询只取需要的字段,必须包含主键id $query->select('id', 'name', 'email'); } ]) ->select( 'id', 'amount', 'customer_type', // morphTo关联必须字段 'customer_id' // morphTo关联必须字段 )->get();
获取数据后可以直接通过对象属性访问关联值:
// 单个订单的客户名称 echo $result->first()->customer->name;
如果需要把客户字段平铺到订单数组中,可以用集合map处理:
$result = $result->map(function ($order) { return [ 'id' => $order->id, 'amount' => $order->amount, 'customer_name' => $order->customer?->name, 'customer_email' => $order->customer?->email, ]; });
方案2:SQL关联查询(适合需要根据客户字段筛选/排序的报表场景)
因为是多态关联两个不同的表,可以通过leftJoin分别关联两个表,用COALESCE函数获取存在的对应字段:
$result = Order::query() ->leftJoin('parents', function ($join) { $join->on('orders.customer_id', '=', 'parents.id') ->where('orders.customer_type', '=', Parent::class); }) ->leftJoin('schools', function ($join) { $join->on('orders.customer_id', '=', 'schools.id') ->where('orders.customer_type', '=', School::class); }) ->select( 'orders.id', 'orders.amount', \DB::raw('COALESCE(parents.name, schools.name) as customer_name'), \DB::raw('COALESCE(parents.email, schools.email) as customer_email') ) ->get();
这种方式可以直接在SQL层面对customer_name、customer_email做排序、筛选操作,适合复杂报表查询场景。
内容的提问来源于stack exchange,提问作者Zakalwe
相关产品推荐
相关产品推荐

