Laravel中如何授权后更新模型且避免两次数据库请求?
解决Laravel中模型实例
update触发MassAssignmentException的问题 问题原因分析
你碰到的这个问题核心是Laravel批量赋值校验机制的差异:
- 用
Question::find($id)拿到的是模型实例,调用->update()时,Laravel会严格校验模型的$fillable(或$guarded)属性,只有在$fillable里声明的字段才能被批量赋值,否则就会抛出MassAssignmentException。 - 而
Question::where('id', $id)返回的是查询构造器,它的update()方法直接生成并执行SQL语句,不会经过模型层的批量赋值校验,所以不会报错,但这种方式跳过了模型的安全校验,并非最佳实践。
最优解决方案:配置模型的批量赋值字段
最规范且无需额外数据库查询的方式,就是在App\Question模型中添加$fillable属性,把允许批量更新的字段列进去:
// app/Question.php class Question extends Model { // 声明允许批量赋值的字段 protected $fillable = ['question', 'type_based_id']; }
修改后你原来的代码(用find()获取模型实例、授权后直接update)就能正常运行,既完成了授权校验,也只发起了一次数据库查询(find()那一次)。
其他可选方案(非必要不推荐)
如果出于特殊原因不想设置$fillable,可以试试以下方式,但要注意安全风险:
逐个赋值后用
save()
这种方式不依赖$fillable,但代码会稍显繁琐:public function update(Request $request, $id) { $question = Question::find($id); $this->authorize('edit', $question); $question->question = $request->question; $question->type_based_id = $request->type_based_id; $question->save(); $updateArray = [ 'id' => $id, 'question' => $request->question, 'type_based_id' => $request->type_based_id, ]; return ['message' => 'Question Updated', 'data' => $updateArray]; }基于已有模型用构造器
update
利用已获取的模型实例的主键构造查询,避免二次查询:public function update(Request $request, $id) { $question = Question::find($id); $this->authorize('edit', $question); $updateArray = [ 'question' => $request->question, 'type_based_id' => $request->type_based_id, ]; // 用已有模型的主键执行update,无需再次查询数据库 Question::where('id', $question->id)->update($updateArray); $updateArray["id"] = $id; return ['message' => 'Question Updated', 'data' => $updateArray]; }不过这种方式会跳过模型的批量赋值校验,同时也不会触发
updated等生命周期钩子,所以除非特殊场景,优先推荐第一种方案。
内容的提问来源于stack exchange,提问作者Witt
相关产品推荐
相关产品推荐

