Laravel控制器多条件块调用validate()跳过后续校验的解决方案
问题根因
Laravel 内置的$request->validate()方法校验失败时会立即抛出ValidationException,框架捕获该异常后会直接终止后续代码执行,重定向回提交页返回错误。你当前把校验逻辑拆分到多个独立的if分支中,只要第一个命中的分支校验不通过,后续分支的校验、数据处理逻辑就完全不会执行,自然会出现你说的校验被跳过的问题。
另外原代码还有三处隐患:
- 在所有校验逻辑执行前就创建了
Application记录并关联用户,一旦后续校验失败,这些提前写入的数据会成为脏数据残留在数据库中 - 雇佣信息写入时
updateOrCreate的查询条件传入了完整的用户模型实例而非用户ID,容易触发关联查询异常 - 所有校验错误提示都统一返回
required,用户无法定位具体是哪个字段漏填
解决方案
不要拆分校验逻辑,先根据用户选中的卡片(即提交的存在字段)动态组装全量校验规则,一次性完成所有字段的校验,校验通过后再执行所有数据库写入操作,既可以保证所有选中卡片的字段都被校验到,也能避免产生脏数据。
修正后的代码如下:
public function save_detail(Request $request) { $rules = []; $messages = []; // 选中雇佣信息卡片时追加对应校验规则 if ($request->has('emp_name')) { $rules = array_merge($rules, [ 'emp_name' => 'required', 'emp_length' => 'required', 'emp_monthly_gross_salary' => 'required', 'monthly_allowance' => 'required', ]); $messages = array_merge($messages, [ 'emp_name.required' => '雇员姓名为必填项', 'emp_length.required' => '雇佣时长为必填项', 'emp_monthly_gross_salary.required' => '月度税前工资为必填项', 'monthly_allowance.required' => '月度补贴为必填项', ]); } // 选中兼职信息卡片时追加对应校验规则 if ($request->has('role')) { $rules = array_merge($rules, [ 'role' => 'required', 'est_earnings' => 'required', ]); $messages = array_merge($messages, [ 'role.required' => '兼职岗位为必填项', 'est_earnings.required' => '预估兼职收入为必填项', ]); } // 选中投资收入卡片时追加对应校验规则 if ($request->has('inv_name')) { $rules = array_merge($rules, [ 'inv_name' => 'required', 'yearly_earning' => 'required', ]); $messages = array_merge($messages, [ 'inv_name.required' => '投资项目名称为必填项', 'yearly_earning.required' => '年度投资收入为必填项', ]); } // 一次性执行全部校验,所有字段错误会统一返回,不会出现校验遗漏 $validated = $request->validate($rules, $messages); // 校验全部通过后再操作数据库,避免产生脏数据 $user = $request->user(); $application = Application::create([ 'app_type_id' => 1, 'approval_status_id' => 1 ]); $application->users()->save($user); if ($request->has('emp_name')) { $user->employment_detail()->updateOrCreate( ['user_id' => $user->id], [ 'emp_name' => $validated['emp_name'], 'emp_length' => $validated['emp_length'], 'emp_monthly_gross_salary' => sanitize_money($validated['emp_monthly_gross_salary']), 'monthly_allowance' => sanitize_money($validated['monthly_allowance']), ] ); } if ($request->has('role')) { $user->part_time()->firstOrCreate([ 'part_time_role' => $validated['role'], 'part_time_earning' => sanitize_money($validated['est_earnings']) ]); } if ($request->has('inv_name')) { $user->borrower_investment_income()->firstOrCreate([ 'invest_source_specify' => $validated['inv_name'], 'invest_year_earning' => sanitize_money($validated['yearly_earning']) ]); } $user->user_current_step = Constant::CURR_STEP_COMMITMENT; $user->save(); alert('success','Your income are saved.'); return redirect()->back(); }
优化说明
- 校验逻辑前置,所有字段校验通过后才执行数据库写入,从根源避免脏数据
- 动态拼接规则保证用户选中多少张卡片,就会校验对应多少组字段,不会出现校验中断遗漏的问题
- 修正了原代码中
updateOrCreate传参的错误,直接使用$user->id作为关联查询条件 - 自定义了明确的错误提示,用户可以直接定位漏填字段
- 校验通过后使用返回的
$validated数组获取字段值,避免直接取Request内容混入非预期字段
内容的提问来源于stack exchange,提问作者yyyyuyu
相关产品推荐
相关产品推荐

