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

为何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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 08:40:09