
本文介绍使用 Laravel Eloquent 的 withSum() 方法,为每位员工关联汇总其考勤表中的 work_time 总和,并在视图中清晰展示,避免 N+1 查询,兼顾性能与可维护性。
本文介绍使用 laravel eloquent 的 `withsum()` 方法,为每位员工关联汇总其考勤表中的 `work_time` 总和,并在视图中清晰展示,避免 n+1 查询,兼顾性能与可维护性。
在 Laravel 应用中,当需要按员工维度统计考勤总工时(如 work_time 字段),直接使用循环 + 单条查询(如 Attendance::where('employe_id', $id)->sum('work_time'))会导致严重的 N+1 查询问题,影响性能。自 Laravel 8.12 起,Eloquent 原生支持 withSum() 关系聚合方法,可在一次查询中为每个员工预加载其关联考勤记录的 work_time 总和,简洁高效。
✅ 正确配置模型关系
首先需修正模型间的关联定义(命名一致性与语法规范):
// app/Models/Employe.php
class Employe extends Model
{
use HasFactory;
protected $fillable = ['id', 'employe_code', 'employe_name', 'workplace'];
// ✅ 关系名应为复数且语义清晰(原 'employee' 已更正为 'attendances')
public function attendances()
{
return $this->hasMany(Attendance::class, 'employe_id');
}
}// app/Models/Attendance.php
class Attendance extends Model
{
use HasFactory;
protected $fillable = [
'id', 'employe_id', 'attendence_date', 'clock_in', 'clock_out', 'work_time', 'attendence_status'
];
// ✅ 修正 belongsTo 关联:类名首字母大写、使用完整命名空间(推荐)
public function employe()
{
return $this->belongsTo(Employe::class, 'employe_id');
}
}⚠️ 注意:
Attendance模型中protected $fillable原代码漏写了=符号,已补全;同时belongsTo推荐传入类名而非字符串路径,以提升 IDE 支持与类型安全。
✅ 控制器:单次查询获取全部员工及工时总和
在控制器中,使用 withSum('relationName', 'column') 即可完成聚合:
// app/Http/Controllers/AttendanceController.php
public function attendance()
{
// ? 一行代码:获取所有员工,并预加载每个员工的 work_time 总和
$employees = Employe::withSum('attendances', 'work_time')->get();
return view('pages.Attendance.hour', compact('employees'));
}该查询将生成类似如下 SQL(经优化):
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
SELECT `employes`.*, (SELECT COALESCE(SUM(`work_time`), 0) FROM `attendances` WHERE `employes`.`id` = `attendances`.`employe_id`) AS `attendances_sum_work_time` FROM `employes`;
✅ 视图:安全渲染总工时
在 Blade 模板中,Eloquent 会自动将聚合结果挂载为 {relation}_sum_{column} 属性(下划线分隔):
<!-- resources/views/pages/Attendance/hour.blade.php -->
<table id="datatable" class="table table-hover table-sm table-bordered p-0" data-page-length="50" style="text-align: center">
<thead>
<tr>
<th class="alert-success">#</th>
<th class="alert-success">员工编号</th>
<th class="alert-success">姓名</th>
<th class="alert-success">累计工时(小时)</th>
</tr>
</thead>
<tbody>
@foreach($employees as $employee)
<tr>
<td>{{ $loop->iteration }}</td>
<td>{{ $employee->employe_code }}</td>
<td>{{ $employee->employe_name }}</td>
<td>{{ number_format($employee->attendances_sum_work_time ?? 0, 2) }} 小时</td>
</tr>
@endforeach
</tbody>
</table>? 提示:
$employee->attendances_sum_work_time默认为null(无考勤时),建议使用空合并运算符?? 0并配合number_format()格式化小数位,提升可读性。
✅ 进阶:按条件筛选或排序
若需仅显示有考勤记录的员工,可链式调用 has():
$employees = Employe::has('attendances')
->withSum('attendances', 'work_time')
->orderByDesc('attendances_sum_work_time')
->get();如需对特定员工 ID 批量查询(非全部),可结合 whereIn():
$employeeIds = [1, 5, 12];
$employees = Employe::whereIn('id', $employeeIds)
->withSum('attendances', 'work_time')
->get();✅ 总结
- ✅ 使用
withSum()是 Laravel 8.12+ 推荐的聚合方式,替代手动SUM()查询,消除 N+1。 - ✅ 关系命名需统一(
attendancesvsemploye),避免混淆;belongsTo推荐使用类引用。 - ✅ 视图中通过
xxx_sum_yyy动态属性访问结果,注意空值处理与格式化。 - ✅ 可灵活组合
has()、orderBy、whereIn等方法,满足复杂业务场景。
此举不仅代码更简洁,也显著提升数据加载效率,是 Laravel 开发中处理“一对多聚合”场景的最佳实践之一。

















