Laravel考勤系统:如何保留旧timeout值并添加新签退时间
问题描述
希望在考勤系统中添加新的签退(timeout)值时保留旧的timeout值——例如初始记录的timein为01:00:11、timeout为01:00:13,当员工再次签退时,旧的timeout会被更新为01:18:18,需要修改逻辑避免覆盖旧值,保留所有签退记录。尝试将代码中的update替换为insert、使用CreateorUpdate均无效。
当前问题代码片段
原签退逻辑中更新记录的代码:
if($attendance) { DB::table('attendances') ->where(['created_by' => $id, 'logdate' => $date] ) ->update([ 'timeout' => $time, 'status'=>'1', ]); }
相关代码与表结构
users表结构
Schema::create('users', function (Blueprint $table) { $table->increments('id'); $table->integer('created_by')->nullable(); $table->string('employee_id')->nullable(); $table->string('name'); $table->string('email', 100)->unique()->nullable(); $table->string('password'); $table->string('P')->default('0'); $table->rememberToken(); $table->timestamps(); });
attendances表结构
Schema::create('attendances', function (Blueprint $table) { $table->increments('id'); $table->integer('created_by'); $table->integer('user_id')->unsigned(); $table->time('timein')->nullable(); $table->time('timeout')->nullable(); $table->date('logdate'); $table->string('status'); $table->timestamps(); });
timeclock方法代码
public function timeclock(Request $request){ $id = $request->input('id'); // dd($id); $date = carbon::today() ; $time = Carbon::now(); $user = DB::table("users") ->where("id", "=", $id) ->first(); if($user == NULL) { $msg = "<div class='alert alert-danger'><button class='close' data-dismiss='alert'>×</button> Please enter your first name to continue!</div>"; } elseif ($user != NULL){ $attendance = DB::table("users") ->leftJoin("attendances", function($join){ $join->on("users.id", "=", "attendances.user_id"); }) ->select("attendances.*", "logdate", "timein", "timeout", "status") ->where("attendances.user_id", "=", $id) ->where("attendances.logdate", "=", $date) ->where("attendances.status", "=", 0) ->get()->toArray(); // dd($attendance); if($user->P == '1') { DB::table('users') ->where(['id' => $id] ) ->update([ 'p'=>'0', ]); } // dd($attendance); if($attendance){ DB::table('attendances') ->where(['created_by' => $id, 'logdate' => $date] ) ->update([ 'timeout' => $time, 'status'=>'1', ]); } else { $result = new Attendance; $result->user_id = $id; $result->created_by = $id; $result->timein = $time; $result->logdate = $date; $result->status = 0; $result->save(); } if($user->P == '0') { DB::table('users') ->where(['id' => $id] ) ->update([ 'p'=>'1', ]); } } return redirect()->route('attendance.timeclockcreate')->with('id', $id)->with('message', 'Inserted successfully.'); }
表单代码
<table class="table table-bordered table-striped"> <thead> <tr> <th> {{ __(' SL') }}</th> {{--<th> {{ __(' EMP CD') }}</th>--}} <th> {{ __(' EMPLOYEE NAME') }}</th> <th> {{ __('TIMEMING') }}</th> <!-- <th> {{ __(' Created At') }} </th> --> </tr> </thead> <tbody> @foreach($attendances as $key => $attd) <tr> <td>{{$key+1}}</td> <td>{{ $attd['name'] }}</td> <td> <form action="{{ route('attendance.timeclock') }}" method="post" name="department_add_form"> {{ csrf_field() }} <input type="hidden" name="id" id="id" value="{{ $attd['id'] }}"> @if($attd['P']=='1') <button type="submit" class="button-88, btn btn-warning" role="button">{{ __('SIGN OUT') }}</button> @elseif($attd['P']=='0') <button type="submit" class="button-88, btn btn-primary" role="button">{{ __('SIGN IN') }}</button> @endif </form> </td> </tr> @endforeach </tbody> </table>
解决方案
核心思路:不再覆盖旧的签退记录,针对不同场景处理:
- 若当天存在未完成(
status=0)的考勤记录,正常更新该记录的签退时间和状态; - 若当天无未完成记录(已签退过),则新增一条新的考勤记录,复用最后一次的签到时间作为当前记录的
timein,同时记录新的签退时间。
修改后的timeclock方法关键代码如下:
public function timeclock(Request $request){ $id = $request->input('id'); $date = Carbon::today(); $time = Carbon::now(); $user = DB::table("users") ->where("id", "=", $id) ->first(); if($user == NULL) { $msg = "<div class='alert alert-danger'><button class='close' data-dismiss='alert'>×</button> Please enter your first name to continue!</div>"; } elseif ($user != NULL){ // 1. 查询当天所有考勤记录 $allAttendances = DB::table("attendances") ->where("user_id", "=", $id) ->where("logdate", "=", $date) ->get()->toArray(); // 2. 查询当天未签退的记录 $unfinishedAttendance = DB::table("attendances") ->where("user_id", "=", $id) ->where("logdate", "=", $date) ->where("status", "=", 0) ->first(); if($user->P == '1') { DB::table('users') ->where(['id' => $id]) ->update(['p'=>'0']); } // 3. 处理考勤记录 if($unfinishedAttendance){ // 存在未签退记录,精准更新该记录 DB::table('attendances') ->where('id', $unfinishedAttendance->id) ->update([ 'timeout' => $time, 'status'=>'1', ]); } else { // 无未签退记录,新增签退记录 $lastTimein = null; if(!empty($allAttendances)){ // 取最后一次签到的时间 $lastAttendance = end($allAttendances); $lastTimein = $lastAttendance->timein; } // 新建记录,若没有历史签到时间则用当前时间作为timein $result = new Attendance; $result->user_id = $id; $result->created_by = $id; $result->timein = $lastTimein ?? $time; $result->timeout = $time; $result->logdate = $date; $result->status = 1; $result->save(); } if($user->P == '0') { DB::table('users') ->where(['id' => $id]) ->update(['p'=>'1']); } } return redirect()->route('attendance.timeclockcreate')->with('id', $id)->with('message', '操作成功.'); }
修改说明
- 拆分原有的关联查询,分别获取“当天所有记录”和“当天未完成记录”,避免冗余;
- 更新记录时使用
id精准定位,防止因created_by和logdate重复导致批量更新错误; - 新增签退记录时复用历史签到时间,保证考勤记录的关联性;
- 调整提示信息适配签到、签退两种场景。
内容的提问来源于stack exchange,提问作者Kairaoi Ientumoa
相关产品推荐
相关产品推荐

