Laravel belongsTo传第三参数后调用属性返回null差异问题
在Laravel模型中编写了两个belongsTo关联方法,代码截图如下:
实际业务调用时存在返回值差异:
- 执行
$user->account()->is_provider返回null - 执行
$user->acc()->is_provider可根据当前登录用户实际数据,正确返回true/false布尔值
两个关联定义的唯一区别是account方法声明belongsTo时额外传入了第三个参数。
核心是传错了belongsTo的第三个参数,先明确Laravel belongsTo的标准方法签名:
public function belongsTo( $related, $foreignKey = null, $ownerKey = null, $relation = null )
各参数作用:
$related:关联的目标模型类,两个方法传入的都是Account模型,这部分无问题$foreignKey:当前模型(即User模型)上存储关联目标ID的外键字段,两个方法都传入account_id,这部分也无问题$ownerKey:关联目标模型(即Account模型)上,用于和外键做值匹配的字段,默认值为目标模型的主键(绝大多数场景是id)$relation:关联名称,框架自动识别,一般无需手动传值
account方法给第三个参数$ownerKey传入了account_id,相当于告诉框架:关联查询时,要查找Account表中account_id字段值等于User表account_id值的记录。常规业务设计下Account表的主键是id,不存在和User外键对应的account_id字段,自然查不到关联的Account记录,最终访问is_provider返回null。
而acc方法没有传入第三个参数,框架默认取Account模型的主键id作为匹配字段,可以正确查到关联的Account数据,因此能返回正确的is_provider值。
当前调用写法不规范:$user->account()返回的是关联查询构造器实例,不是关联模型结果,正确获取关联模型的写法是使用动态属性(不带括号):
// 正确写法,直接返回关联的Account模型实例 $user->account->is_provider; // 带括号返回查询构造器,适合追加额外查询条件,例如 $user->account()->where('status', 1)->first();
带括号调用acc时能拿到正确值,只是查询构造器不存在is_provider属性时触发了框架的动态加载逻辑,属于误打误撞,不推荐这种写法。
如果Account表使用id作为主键和User的account_id做关联,直接删掉account方法里的第三个参数即可,两个方法逻辑完全一致:
public function account() { return $this->belongsTo(Account::class, 'account_id'); }
如果Account表确实使用非主键字段account_id和User做关联,先确认Account表存在该字段,且字段值和User表的account_id可以一一对应,否则传入该参数只会导致关联查询失败。
内容的提问来源于stack exchange,提问作者DevfaR

