Laravel 5.6控制器Store方法验证员工预约不重复求助
嘿,这个需求我之前也帮人处理过,在Laravel 5.6里实现起来很清晰,给你两种方案参考,按需选就行~
方案一:直接在Store方法中做检查
这是最直接的方式,适合逻辑简单的场景,直接在资源控制器的store方法里先验证基础字段,再检查重复:
public function store(Request $request) { // 先做基础表单验证,确保必填字段合法 $validatedData = $request->validate([ 'day' => 'required|date', 'employee_id' => 'required|exists:employees,id', 'resource_id' => 'required|exists:resources,id', ]); // 检查该员工当天是否已有预约 $appointmentExists = Appointment::where('day', $request->day) ->where('employee_id', $request->employee_id) ->exists(); if ($appointmentExists) { // 回退到创建页面,携带错误信息和用户之前的输入 return back()->withErrors([ 'employee_id' => '该员工在所选日期已有预约,请选择其他日期或员工。' ])->withInput(); } // 没有重复就创建新预约 Appointment::create($validatedData); // 成功后跳转到预约列表页,带成功提示 return redirect()->route('appointments.index')->with('success', '预约创建成功!'); }
方案二:用自定义验证规则(更优雅,可复用)
如果这个验证逻辑以后可能在其他地方用到(比如编辑预约时也要检查),那自定义验证规则会更优雅,也方便复用:
首先创建自定义规则:
php artisan make:rule EmployeeHasNoAppointmentOnDay
然后打开生成的app/Rules/EmployeeHasNoAppointmentOnDay.php,写入验证逻辑:
<?php namespace App\Rules; use Illuminate\Contracts\Validation\Rule; use App\Appointment; class EmployeeHasNoAppointmentOnDay implements Rule { protected $employeeId; // 接收员工ID作为参数 public function __construct($employeeId) { $this->employeeId = $employeeId; } // 验证逻辑:检查该员工当天是否无预约 public function passes($attribute, $value) { return !Appointment::where('day', $value) ->where('employee_id', $this->employeeId) ->exists(); } // 验证失败时的提示信息 public function message() { return '该员工在所选日期已有预约,请选择其他日期或员工。'; } }
接下来在store方法里使用这个规则:
public function store(Request $request) { $validatedData = $request->validate([ 'day' => [ 'required', 'date', // 实例化自定义规则,传入员工ID new EmployeeHasNoAppointmentOnDay($request->employee_id) ], 'employee_id' => 'required|exists:employees,id', 'resource_id' => 'required|exists:resources,id', ]); Appointment::create($validatedData); return redirect()->route('appointments.index')->with('success', '预约创建成功!'); }
视图层显示错误信息
不管用哪种方案,都要在预约创建页面(create.blade.php)的表单里加上错误提示,这样用户能直观看到哪里出错了,并且保留之前输入的内容:
<div class="form-group"> <label for="employee_id">选择员工</label> <select name="employee_id" id="employee_id" class="form-control @error('employee_id') is-invalid @enderror"> <!-- 这里循环输出员工选项 --> @foreach($employees as $employee) <option value="{{ $employee->id }}" {{ old('employee_id') == $employee->id ? 'selected' : '' }}> {{ $employee->name }} </option> @endforeach </select> @error('employee_id') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> <div class="form-group"> <label for="day">预约日期</label> <input type="date" name="day" id="day" class="form-control @error('day') is-invalid @enderror" value="{{ old('day') }}"> @error('day') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> <!-- 其他字段(比如resource_id)的表单也类似处理 -->
数据库层面的双重保障(可选但推荐)
为了防止极端情况下的并发提交(比如两个用户同时给同一个员工预约同一天),最好在数据库层面加唯一索引,从根源上避免重复:
创建一个新的迁移文件:
php artisan make:migration add_unique_index_to_appointments
打开迁移文件,添加唯一索引:
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddUniqueIndexToAppointments extends Migration { public function up() { Schema::table('appointments', function (Blueprint $table) { // 给day和employee_id组合添加唯一索引 $table->unique(['day', 'employee_id']); }); } public function down() { Schema::table('appointments', function (Blueprint $table) { $table->dropUnique(['day', 'employee_id']); }); } }
执行迁移:
php artisan migrate
之后如果数据库因为唯一约束报错,我们可以在store方法里捕获这个异常,给用户友好提示:
try { Appointment::create($validatedData); } catch (\Illuminate\Database\QueryException $e) { // 23000是MySQL唯一约束违反的错误码 if ($e->getCode() == 23000) { return back()->withErrors([ 'employee_id' => '该员工在所选日期已有预约,请选择其他日期或员工。' ])->withInput(); } // 其他异常继续抛出,方便排查问题 throw $e; }
这样就实现了代码和数据库的双重验证,完全不用担心重复排班的问题啦~
内容的提问来源于stack exchange,提问作者Ajkea
相关产品推荐
相关产品推荐

