Laravel 文档里提到过 withDefault() 可以给 belongsTo 关联返回一个空模型。但很少人知道,你可以在里面直接定义属性,甚至支持闭包逻辑,从而彻底消除“试图访问非对象属性”的报错。

💡 场景示例

假设 User 模型有一个 Profile 关联,但并不是每个用户都填写了资料。

常规做法(普通默认值):

public function profile()
{
    return $this->belongsTo(Profile::class)
            ->withDefault();
}
// 结果:$user->profile->bio 会返回 null,但不会报错。

隐藏技巧:预设默认属性

你可以直接在 withDefault 中传入数组,或者使用闭包来动态生成默认状态。

public function profile()
{
    return $this->belongsTo(Profile::class)->withDefault([
        'bio' => '这个用户很懒,什么都没写。',
        'avatar' => 'default-avatar.png',
        'membership_level' => 'Free',
    ]);
}

🛠️ 进阶:使用闭包

如果默认值需要依赖逻辑判断:

public function profile()
{
    return $this->belongsTo(Profile::class)->withDefault(function ($profile, $user) {
        $profile->bio = "新加入的第 {$user->id} 位成员";

        if ($user->is_admin) {
            $profile->membership_level = 'VIP';
        }
    });
}

🎯 为什么要用它?

  1. 视图层零负担:Blade 模板可以直接写 {{ $user->profile->bio }},不需要判断 optional() 或使用 ??
  2. 保持一致性:无论关联是否存在,$user->profile 始终返回一个模型实例,你可以放心地在它上面调用模型方法。
  3. 解耦逻辑:默认值的逻辑被封装在模型层,而不是散落在各个视图或控制器中。