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

Laravel 8 Eloquent左关联软删除表如何保留全部主表数据?

问题根因

你原来的写法中whereNull('phone.deleted_at')是加在全局WHERE条件里的,执行逻辑是:先做左关联,再过滤所有关联结果中phone.deleted_at不为空的行。如果学生名下所有手机号都被软删除、或者没有手机号,关联出来的行要么phone.deleted_at有值、要么所有phone字段为NULL,都会被全局WHERE条件过滤,相当于左关联实际生效为内关联,自然就丢了这部分学生数据。

修复代码

把phone表的软删除过滤条件挪到左关联的条件中,不要放在全局WHERE里,同时可以优化分组逻辑,把多个手机号拼接成单个字段避免同一个学生出现多行数据:

$data = Student::select(['student.name', 'student.email', \DB::raw('GROUP_CONCAT(phone.number SEPARATOR ",") as phone_number')])
    ->leftJoin('phone', function ($join) {
        $join->on('student.id', '=', 'phone.student_id')
            ->whereNull('phone.deleted_at'); // 软删除过滤放在关联条件内
    })
    ->groupBy('student.id', 'student.name', 'student.email') // 移除不需要的phone.deleted_at分组字段
    ->get();
逻辑说明
  • 关联条件内的whereNull('phone.deleted_at')只会过滤phone表的关联数据,不会影响主表student的行,没有有效手机号的学生关联出来的phone_number会自动为NULL,符合你留空的需求
  • 用GROUP_CONCAT拼接多个手机号,是为了避免同一个学生有多个有效手机号时导出多条重复的学生基础信息,如果你确实需要每个手机号对应一行数据,删掉GROUP_CONCAT和groupBy即可,此时没有有效手机号的学生也会保留,phone_number为NULL
  • 如果你的Phone模型本身已经引入了软删除特质(use SoftDeletes),也可以直接用Laravel关联关系来实现,代码更简洁:
    先在Student模型中定义关联:
    public function phones()
    {
        return $this->hasMany(Phone::class); // 模型自带软删除过滤,不需要额外写whereNull
    }
    
    导出查询直接用预加载:
    $data = Student::with('phones')->get()->map(function ($student) {
        return [
            'name' => $student->name,
            'email' => $student->email,
            'phone_number' => $student->phones->pluck('number')->join(','),
        ];
    });
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 01:54:02