Laravel实现工作评分总分存储及按供应商ID计算平均分方案
Laravel 工作评分持久化与供应商平均分计算落地方案
1. 数据库结构调整
首先新增total_score字段存储单条工作的总分,同时回填历史数据:
- 生成迁移文件
执行Artisan命令创建迁移:
php artisan make:migration add_total_score_to_pekerjaans_table --table=pekerjaans
- 编辑迁移文件
打开database/migrations/目录下刚生成的迁移文件,写入字段新增与历史数据回填逻辑:
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; use App\Models\Pekerjaan; return new class extends Migration { public function up() { Schema::table('pekerjaans', function (Blueprint $table) { // 总分字段放在nilai_4之后,默认值0 $table->integer('total_score')->default(0)->after('nilai_4'); }); // 回填历史数据的总分 Pekerjaan::chunk(100, function ($list) { foreach ($list as $item) { $item->total_score = $item->nilai_1 + $item->nilai_2 + $item->nilai_3 + $item->nilai_4; $item->saveQuietly(); } }); } public function down() { Schema::table('pekerjaans', function (Blueprint $table) { $table->dropColumn('total_score'); }); } };
- 执行迁移生效
php artisan migrate
2. 模型层逻辑配置
2.1 Pekerjaan(工作)模型配置
- 将
total_score加入批量赋值白名单 - 注册模型保存事件,新增/更新时自动计算总分,无需每次手动编写重复计算逻辑
打开app/Models/Pekerjaan.php,添加如下代码:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Pekerjaan extends Model { // 加入total_score到fillable数组 protected $fillable = [ 'penyedia_id', 'nilai_1', 'nilai_2', 'nilai_3', 'nilai_4', 'total_score', // 其余原有字段保留即可 ]; // 模型启动时注册自动计算事件 protected static function booted() { // 新增、更新记录前自动计算总分写入 static::saving(function ($pekerjaan) { $pekerjaan->total_score = collect([ $pekerjaan->nilai_1, $pekerjaan->nilai_2, $pekerjaan->nilai_3, $pekerjaan->nilai_4 ])->sum(); }); } // 原有与供应商的关联保留 public function penyedia() { return $this->belongsTo(Penyedia::class); } }
2.2 Penyedia(供应商)模型配置
添加平均分访问器,直接基于已持久化的total_score计算,避免视图层写零散的计算逻辑:
打开app/Models/Penyedia.php,添加如下代码:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Penyedia extends Model { // 如果需要JSON/API输出自动携带平均分,可加入appends数组 protected $appends = ['average_score']; // 原有一对多关联保留 public function pekerjaans() { return $this->hasMany(Pekerjaan::class); } // 平均分访问器,默认保留2位小数 public function getAverageScoreAttribute() { // 无工作记录时返回0,避免null导致页面报错 return round($this->pekerjaans()->avg('total_score') ?? 0, 2); } }
3. 控制器层优化
原有评分更新逻辑无需大幅改动,模型事件会自动处理总分持久化;查询供应商列表时增加预加载避免N+1性能问题:
// 评分更新方法(原有逻辑微调即可) public function updateScore(Request $request, Pekerjaan $pekerjaan) { $validated = $request->validate([ 'nilai_1' => 'required|integer|min:0|max:100', 'nilai_2' => 'required|integer|min:0|max:100', 'nilai_3' => 'required|integer|min:0|max:100', 'nilai_4' => 'required|integer|min:0|max:100', ]); $pekerjaan->update($validated); // 无需手动计算total_score,模型saving事件自动完成写入 return back()->with('success', '评分更新成功'); } // 供应商列表查询方法 public function penyediaList() { // 预加载工作记录,避免循环查询平均分触发N+1问题 $penyediaList = Penyedia::with('pekerjaans')->get(); return view('penyedia.index', compact('penyediaList')); }
4. Blade视图调用
删除原有临时计算总分、平均分的零散逻辑,直接调用模型属性即可:
- 单条工作总分展示:
<!-- 替换原有临时计算的{{ $item->nilai_1 + $item->nilai_2 + $item->nilai_3 + $item->nilai_4 }} --> <tr> <td>{{ $item->nama_pekerjaan }}</td> <td>{{ $item->nilai_1 }}</td> <td>{{ $item->nilai_2 }}</td> <td>{{ $item->nilai_3 }}</td> <td>{{ $item->nilai_4 }}</td> <td>*{{ $item->total_score }}*</td> </tr>
- 供应商平均分展示:
@foreach($penyediaList as $penyedia) <tr> <td>{{ $penyedia->nama_penyedia }}</td> <td>{{ $penyedia->pekerjaans->count() }} 条工作记录</td> <td>*{{ $penyedia->average_score }}*</td> </tr> @endforeach
注意事项
- 若使用批量更新语法(如
Pekerjaan::where(...)->update(...))不会触发模型事件,需在批量更新后手动重算对应记录的总分 - 若评分字段为浮点数,把迁移里的
integer类型改成float/decimal即可,上层计算逻辑无需调整
内容的提问来源于stack exchange,提问作者YuPi
相关产品推荐
相关产品推荐

