为何Laravel Eloquent使用join时with()无法加载morphOne关联数据?
问题分析与解决方案
问题原因
你的leftJoin触发了字段名称冲突:featureds表的主键id和companies表的主键id重名,执行join后,查询结果中的id字段会被featureds.id覆盖。Eloquent加载模型时,会把这个被覆盖的id当作Company模型的主键值,预加载socials关联时,就会用错误的id(要么是featured记录的id,要么是null)去查询关联数据,因此只有当公司有匹配的featured记录时,才能碰巧用正确的id查到socials。
解决方案
方案1:明确指定查询字段,避免冲突
在JoinWithActiveFeatured作用域中添加select('companies.*'),确保只选择companies表的字段,不会被featureds表的字段覆盖:
public function scopeJoinWithActiveFeatured(Builder $query): Builder { return $query->leftJoin('featureds', function ($join) { $join->on('featureds.featureable_id', '=', 'companies.id') ->whereNotNull('featureds.featureable_id') ->whereDate('featureds.featured_from', '<=', now()) ->whereDate('featureds.featured_until', '>=', now()); })->select('companies.*'); // 新增该行,锁定查询字段 }
这样Eloquent就能正确获取Company的主键id,预加载socials时会用正确的id查询关联数据。
方案2:改用whereExists实现排序需求(可选)
如果join仅为了实现"推荐公司优先排序"的需求,可以不用join,改用whereExists配合orderByRaw实现,彻底避免字段冲突:
public function scopeWithActiveFeaturedSort(Builder $query): Builder { return $query->orderByRaw("EXISTS( SELECT 1 FROM featureds WHERE featureds.featureable_id = companies.id AND featureds.featureable_type = ? AND featureds.featured_from <= NOW() AND featureds.featured_until >= NOW() ) DESC", [Company::class]) ->orderBy('companies.name'); }
查询代码调整为:
$companies = Company::query() ->when($request->filled('search'), fn($query) => $query->where('name', 'like', '%' . $request->input('search') . '%')) ->withActiveFeaturedSort() ->with(['socials']) ->paginate(6);
这种方式无需join,既满足排序需求,又不会干扰关联数据的预加载。
内容的提问来源于stack exchange,提问作者csandreas1
相关产品推荐
相关产品推荐

