Laravel考勤系统:如何获取指定日期未提交考勤的班级与Section
Laravel考勤系统:获取指定日期未提交考勤的班级与Section
问题背景
在Laravel中开发考勤系统,需要区分指定日期下已提交考勤和未提交考勤的班级与Section。相关表结构如下:
- students表:存储学生数据,包含
clas_name(班级)与section_name(Section)字段; - attendence_dates表:记录已提交考勤的班级信息,标记当日班级是否上课(
working)、考勤是否最终提交(final),表迁移代码:
public function up() { Schema::create('attendence_dates', function (Blueprint $table) { $table->bigIncrements('id'); $table->date('date')->default(date("Y-m-d")); $table->string('clas_name', 20)->nullable(); $table->string('section_name',20)->nullable(); $table->boolean('working')->default(false); $table->boolean('final')->default(false); $table->string('remarks', 100)->nullable(); $table->unsignedBigInteger('updated_by')->nullable(); $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); }); }
- attendences表:存储学生具体考勤状态(
P为出勤,A为缺勤),表迁移代码:
public function up() { Schema::create('attendences', function (Blueprint $table) { $table->bigIncrements('id'); $table->integer('att_month')->default('0')->nullable(); $table->date('att_date')->useCurrent(); $table->string('clas_name', 20)->nullable(); $table->string('section_name',20)->nullable(); $table->unsignedBigInteger('student_id')->nullable(); $table->unsignedBigInteger('acc_id')->nullable(); $table->enum('status', ['P', 'A'])->default('P'); $table->foreign('student_id')->references('id')->on('students') ->onDelete('SET NULL') ->onUpdate('CASCADE'); $table->foreign('acc_id')->references('acc_id')->on('students') ->onDelete('SET NULL') ->onUpdate('CASCADE'); $table->timestamps(); }); }
- class表:存储班级基础信息,表迁移代码:
public function up() { Schema::create('class', function (Blueprint $table) { $table->Increments('id'); $table->string('name', 50); $table->string('full_name', 50); $table->boolean('status')->default(1); $table->timestamps(); }); }
- sections表:存储Section基础信息,表迁移代码:
public function up() { Schema::create('sections', function (Blueprint $table) { $table->integer('id',true,true); $table->string('name', 50); $table->string('short_name', 50); $table->boolean('status')->default(1); $table->timestamps(); }); }
当前实现与问题
已通过以下代码获取所有有学生的班级与Section:
$classAll = DB::table('students') ->select('clas_name', 'section_name') ->groupBy('clas_name','section_name') ->orderBy('clas_name','ASC') ->orderBy('section_name','ASC') ->get();
通过以下代码获取指定日期已提交考勤的班级与Section:
// 获取已提交考勤的班级数据 $posted = DB::table('attendence_dates') ->select('clas_name', 'section_name') ->where('date', $date) ->groupBy('clas_name','section_name') ->orderBy('clas_name','ASC') ->orderBy('section_name','ASC') ->get();
尝试获取未提交考勤的班级与Section时:
- 使用
$pending = $classAll->diff($posted);报错:Object of class stdClass could not be converted to string,因为diff默认会把对象转为字符串比较,无法正确识别两个对象是否代表同一班级+Section; - 使用
$pending = $classAll->diffKeys($posted);无报错但结果错误,因为diffKeys只比较集合的键(即数组索引),而非实际的班级和Section值。
补充说明
选择从students表获取班级与Section,而非class和sections表,是因为class表可能存在无学生的空班级,且部分班级的Section数量不一致,直接从students表读取更符合实际业务场景。
解决方案
方法一:数据库查询(推荐,效率更高)
直接通过SQL查询获取未提交的班级与Section,避免在内存中处理集合:
$pending = DB::table('students') ->select('clas_name', 'section_name') ->groupBy('clas_name', 'section_name') ->whereNotExists(function ($query) use ($date) { $query->select(DB::raw(1)) ->from('attendence_dates') ->whereColumn('attendence_dates.clas_name', 'students.clas_name') ->whereColumn('attendence_dates.section_name', 'students.section_name') ->where('attendence_dates.date', $date); }) ->orderBy('clas_name', 'ASC') ->orderBy('section_name', 'ASC') ->get();
这个查询利用whereNotExists,找出所有在students表中存在,但在指定日期的attendence_dates表中没有记录的班级+Section组合。
方法二:Laravel Collection处理
如果需要在内存中处理集合,可以使用diffUsing方法自定义比较逻辑,判断两个对象的clas_name和section_name是否都相同:
$pending = $classAll->diffUsing($posted, function ($a, $b) { // 先比较班级名称,再比较Section名称 $classCompare = strcmp($a->clas_name, $b->clas_name); if ($classCompare !== 0) { return $classCompare; } return strcmp($a->section_name, $b->section_name); });
diffUsing允许你自定义两个元素的比较规则,返回0表示两个元素相等,非0则表示不等,这样就能正确筛选出未提交的班级与Section。
内容的提问来源于stack exchange,提问作者Vehlad
相关产品推荐
相关产品推荐

