Laravel关联关系场景下如何正确使用firstOrCreate方法?
在Laravel Eloquent中结合关联模型使用firstOrCreate的正确姿势
没问题,这种场景完全可以用firstOrCreate!我来帮你分析下问题出在哪,以及怎么解决:
先修正模型里的小问题
你的Athlete模型中,$fillable数组包含了'nation',但这个是关联关系的名称,不是数据库实际存储的字段——数据库里对应的外键应该是nation_id(这是Eloquent belongsTo关联的默认命名规则)。所以首先要调整Athlete的$fillable:
class Athlete extends Model { public $timestamps = false; // 把nation_id加入可填充字段,移除不需要的'nation'(如果数据库没有这个字段的话) protected $fillable = ['firstname', 'lastname', 'nation_id', 'sex']; public function nation() { return $this->belongsTo('App\Nation'); } }
两种正确的实现方式
方式一:直接通过Athlete模型调用
修正$fillable之后,你第一种写法的思路是对的,现在可以正常运行了:
$ath = Athlete::firstOrCreate([ 'nation_id' => $nation->id, 'lastname' => $nn, 'sex' => $sex, 'firstname' => $vn ]);
之前报错的原因
之前的错误"Nation_id doesn't have a default value",是因为nation_id不在$fillable数组中,当创建新记录时,Laravel不会将这个字段赋值到模型里,导致数据库插入时缺少必填的外键字段。
方式二:通过关联关系调用(更推荐)
利用Eloquent的关联关系来操作会更优雅,Laravel会自动帮你处理nation_id的赋值,不需要手动传入:
$ath = $nation->athletes()->firstOrCreate([ 'lastname' => $nn, 'sex' => $sex, 'firstname' => $vn ]);
这种方式的好处是,你不需要在查询条件里手动写nation_id,关联关系会自动把当前$nation的ID绑定到新创建的Athlete记录上,同时也规避了$fillable的限制(因为关联创建外键时会绕过fillable检查)。
为什么你的第二种写法不行?
你尝试直接传入['nation' => $nation]作为查询条件,这是错误的——firstOrCreate的参数需要的是数据库字段的键值对,而不是关联模型对象。Laravel会尝试把这个对象转换成字符串来构建WHERE语句,这显然不符合数据库查询的要求,所以会失败。
内容的提问来源于stack exchange,提问作者gnarf




