hasone和belongsto必须成对写在正确模型上,外键在哪张表hasone就写在哪边;with()参数必须严格匹配方法名;外键或主键非id时需显式传三个参数;迁移中必须加unique()和索引,否则关联失效。

hasOne 和 belongsTo 必须成对写在正确模型上,否则关联永远返回 null,且不报错。
外键在哪张表,hasOne 就写在哪边
这是最容易翻车的第一步。Laravel 不靠“名字”判断关系,而靠外键物理位置。
- 如果
profiles表里有user_id字段 →hasOne写在User模型里(User “拥有” Profile) - 如果
users表里有profile_id字段 →hasOne就得写在Profile模型里(Profile “拥有” User,反向设计) - 写反了不会报错,但
$user->profile会是null,SQL 里 WHERE 条件查的是profile_id而不是你实际的user_id
with('profile') 不生效?检查方法名和大小写
with() 是字符串匹配,不是自动推导。它只认你模型里定义的方法名,一个字母都不能错。
- 模型里写的是
public function profile()→ 必须用with('profile') - 写成
with('Profile')、with('userProfile')或with('profiles')都静默失败 - 关联属性访问(如
$user->profile)会触发懒加载,但with()不加,100 个用户就发 100 次 SQL - 想查「有 profile 的用户」,要用
whereHas('profile'),不是with()+ 空值判断
外键或主键不是 id?三个参数一个都不能省
默认约定只适用于最简场景。只要字段名或主键名一变,就必须显式传参,否则正查能过、反查崩。
-
hasOne(Profile::class, 'owner_id', 'uid'):表示外键是owner_id(在 profiles 表),本地主键是uid(在 users 表) -
belongsTo(User::class, 'owner_id', 'uid'):第二、三参数必须跟上面完全一致,否则$profile->user查不到 - 字段类型也要对齐:比如
user_id是BIGINT UNSIGNED,users.id也得是同类型,否则 MySQL 隐式转换会让关联失效
迁移里漏掉 unique(),业务逻辑就不可控
一对一不是 PHP 层控制的,是数据库约束保证的。没加唯一索引,代码看着对,数据早乱了。
-
$table->foreignId('user_id')->unique()->constrained()->onDelete('cascade');是标准写法 - 不加
unique(),$user->profile()->create()可以成功执行多次,一个用户存多条 profile - 这时
$user->profile懒加载可能随机返回某一条(MySQL 无序),with('profile')只取第一条,行为不可预测 - 外键字段本身还要加索引:
$table->index('user_id'),否则whereHas()会全表扫描
unique() 这三件事——它们不报错,但让关联形同虚设。











