You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel按月统计员工出勤天数问题及代码优化需求

修复Laravel按月统计员工出勤天数的功能

我看了你的代码,现在的问题主要集中在没有按选中的年月筛选请假记录、视图里的日期判断逻辑错误,还有一些可以优化的地方。下面是修正后的完整实现:

第一步:优化控制器代码

首先,我们用Laravel自带的Carbon来计算工作日(比自定义函数更简洁可靠),同时按选中的年月筛选请假记录,避免加载所有数据:

use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

public function employeeAttendance(Request $request) {
    // 获取选中的年月,默认设为当前年月
    $countMonth = $request->get('month', date('m'));
    $countYear = $request->get('year', date('Y'));

    // 计算当月工作日总数(排除周六周日)
    $startDate = Carbon::create($countYear, $countMonth, 1);
    $endDate = $startDate->copy()->endOfMonth();
    $totalWorkingDays = $startDate->diffInDaysFiltered(function(Carbon $date) {
        return !$date->isWeekend();
    }, $endDate);

    // 获取所有非管理员员工
    $employees = User::where('username', '!=', 'admin')->get();

    // 按年月统计每个员工的请假天数
    $leaveStats = LeaveManagement::whereYear('start', $countYear)
        ->whereMonth('start', $countMonth)
        ->select('name', 'type', DB::raw('count(*) as total'))
        ->groupBy('name', 'type')
        ->get()
        ->groupBy('name');

    return view('pages.attendance', compact('employees', 'totalWorkingDays', 'leaveStats', 'countMonth', 'countYear'));
}

控制器改进点:

  • 用Carbon替代自定义的countDays函数,代码更简洁且不易出错
  • 只查询选中年月的请假记录,并用分组统计直接算出每个员工的事假/病假数量,避免视图里的嵌套循环
  • 提前过滤掉管理员用户,减少视图里的冗余判断

第二步:修正视图代码

接下来修复视图里的选中状态、请假天数显示逻辑:

<div class="col-md-5 align-self-center">
    <h4 class="text-themecolor">{{__('Employee Attendance')}}</h4>
</div>
</div>
<div class="card">
    <div class="card-body">
        <form action="{{ route('employee_attendance') }}" method="GET">
            <select class="custom-select col-md-2" name="month">
                <option value="">Select Month</option>
                <option value="01" {{ $countMonth == '01' ? 'selected' : '' }}>January</option>
                <option value="02" {{ $countMonth == '02' ? 'selected' : '' }}>February</option>
                <option value="03" {{ $countMonth == '03' ? 'selected' : '' }}>March</option>
                <option value="04" {{ $countMonth == '04' ? 'selected' : '' }}>April</option>
                <option value="05" {{ $countMonth == '05' ? 'selected' : '' }}>May</option>
                <option value="06" {{ $countMonth == '06' ? 'selected' : '' }}>June</option>
                <option value="07" {{ $countMonth == '07' ? 'selected' : '' }}>July</option>
                <option value="08" {{ $countMonth == '08' ? 'selected' : '' }}>August</option>
                <option value="09" {{ $countMonth == '09' ? 'selected' : '' }}>September</option>
                <option value="10" {{ $countMonth == '10' ? 'selected' : '' }}>October</option>
                <option value="11" {{ $countMonth == '11' ? 'selected' : '' }}>November</option>
                <option value="12" {{ $countMonth == '12' ? 'selected' : '' }}>December</option>
            </select>
            <select class="custom-select col-md-2" name="year">
                <option value="">Select Year</option>
                <?php for ($year = 2000; $year <= 2050; $year++) {
                    $selected = ($countYear == $year) ? 'selected' : '';
                    echo "<option value=$year $selected>$year</option>";
                } ?>
            </select>
            <button class="btn btn-info" type="submit"><i class="fa fa-search "></i></button>
            <span style="float:right" class = "btn btn-info" disabled>Total Working Days : {{ $totalWorkingDays }} </span>
        </form>
        <div class="table-responsive m-t-40">
            <table class="table table-bordered table-striped ">
                <thead>
                    <tr>
                        <th>Employee Name</th>
                        <th>Present Day</th>
                        <th>Casual Leave</th>
                        <th>Medical Leave</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach ($employees as $emp)
                        @php
                            $empName = $emp['personal_detail']['first_name'];
                            // 获取该员工当月的请假统计,默认0
                            $casualCount = $leaveStats->get($empName)?->where('type', 'casual')->first()?->total ?? 0;
                            $medicalCount = $leaveStats->get($empName)?->where('type', 'medical')->first()?->total ?? 0;
                            $totalLeave = $casualCount + $medicalCount;
                            $presentDay = $totalWorkingDays - $totalLeave;
                            // 确保出勤天数不为负数
                            $presentDay = max($presentDay, 0);
                        @endphp
                        <tr>
                            <td> {{ $empName }} </td>
                            <td> {{ $presentDay }} </td>
                            <td> {{ $casualCount }} </td>
                            <td> {{ $medicalCount }} </td>
                        </tr>
                    @endforeach
                </tbody>
            </table>
        </div>
    </div>
</div>
</div>
</div>

视图改进点:

  • 修复了年月选择框的选中状态,现在选中后会保持高亮
  • 移除了嵌套循环,直接用控制器提前统计好的$leaveStats获取每个员工的请假数,性能更好
  • 修复了之前视图里日期判断的错误(之前在循环外引用$empleave会拿到最后一条记录的日期)
  • 添加了max($presentDay, 0)确保出勤天数不会出现负数(比如请假天数超过工作日的情况)

额外建议

如果你的LeaveManagement模型和User模型之间有关联(比如user_id字段),建议用关联查询替代按name匹配,这样更可靠(避免重名问题):

  1. 在LeaveManagement模型里添加关联:public function user() { return $this->belongsTo(User::class); }
  2. 控制器里的请假统计改为按user_id分组,视图里用用户ID匹配

内容的提问来源于stack exchange,提问作者amin devloper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:23:45