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

Laravel中User、Discipline、WorkHours模型Eloquent关系合理性咨询

问题:Laravel Eloquent模型关系设计咨询

我正在为所属俱乐部开发一款基于Laravel/Vue/Inertia的管理工具,需要为三个模型设计合理的Eloquent关系,业务场景如下:

  • 俱乐部划分为多个Discipline(部门/领域),每位成员每年需完成一定时长的志愿工时
  • 成员提交工时需填写Discipline、日期、时长,由该Discipline的负责人或指定审批人确认
  • 创建WorkHours记录时,需填写日期、工时数、Discipline及对应审批人
  • 注意:一个用户可同时作为某Discipline的审批人及提交WorkHours的成员,但同一条记录中不能兼具两种身份

我的初步设计如下:

模型关系逻辑

  1. Discipline与User为多对多关系(一个Discipline可拥有多个审批人,一个用户可成为多个Discipline的审批人)
  2. 一条WorkHours记录仅属于一个用户(user_id)且仅对应一个审批人(approver_id)
  3. 一个用户可拥有多条WorkHours记录,且可属于多个Discipline

模型定义

  • Discipline模型:包含name字段,关联方法:
    public function approvers()
    {
        return $this->belongsToMany(User::class);
    }
    
  • WorkHours模型:包含date、hours字段,关联方法:
    public function member()
    {
        return $this->belongsTo(User::class); // 对应user_id
    }
    public function discipline()
    {
        return $this->belongsTo(Discipline::class);
    }
    public function approver()
    {
        return $this->hasOne(User::class); // 对应approver_id
    }
    
  • User模型:关联方法:
    public function disciplines()
    {
        return $this->belongsToMany(Discipline::class);
    }
    public function workHours()
    {
        return $this->hasMany(WorkHours::class);
    }
    

数据库表结构

  • work_hours表:id、user_id、approver_id
  • disciplines表:id、name
  • users表:id、name
  • 中间表disciplines_users:user_id、discipline_id

请问该设计是否正确?是否有更优的关系定义方式?


回答

一、初步设计的问题点

  1. WorkHours的approver关联错误:approver()方法应该用belongsTo(User::class)而非hasOne(User::class)。因为一条WorkHours记录属于一个审批人(approver_id是外键指向users表),hasOne是反向关联的用法,这里逻辑完全颠倒。
  2. 数据库表缺失字段:work_hours表缺少discipline_id字段,模型里定义了discipline()关联但表结构无对应字段,会导致关联无法正常工作。
  3. 中间表扩展性不足:当前disciplines_users仅存关联ID,若未来需区分用户在Discipline中的角色(如负责人/普通审批人),会缺少扩展空间。

二、优化后的模型关系设计

1. 模型关联修正

  • WorkHours模型:修正关联逻辑,明确外键对应关系:

    public function member()
    {
        return $this->belongsTo(User::class, 'user_id');
    }
    
    public function discipline()
    {
        return $this->belongsTo(Discipline::class, 'discipline_id');
    }
    
    public function approver()
    {
        return $this->belongsTo(User::class, 'approver_id');
    }
    
  • User模型:补充审批记录关联,便于查询审批人处理的所有工时:

    public function disciplines()
    {
        return $this->belongsToMany(Discipline::class);
    }
    
    public function submittedWorkHours()
    {
        return $this->hasMany(WorkHours::class, 'user_id');
    }
    
    public function approvedWorkHours()
    {
        return $this->hasMany(WorkHours::class, 'approver_id');
    }
    
  • Discipline模型:补充该领域下的工时记录关联:

    public function approvers()
    {
        return $this->belongsToMany(User::class);
    }
    
    public function workHours()
    {
        return $this->hasMany(WorkHours::class);
    }
    

2. 数据库表修正

  • work_hours表新增discipline_id字段,最终结构:
    id (主键)
    user_id (外键,关联users.id)
    discipline_id (外键,关联disciplines.id)
    approver_id (外键,关联users.id)
    date (日期类型)
    hours (数值类型,如decimal)
    created_at, updated_at (Laravel时间戳,可选)
    
  • 可选优化:给disciplines_users中间表新增role字段(如approver/member),用于区分用户在Discipline中的身份,提升扩展性。

3. 业务规则代码实现

针对“同一条记录中提交人不能是审批人”的规则,在WorkHours的验证逻辑中添加校验:

// 在WorkHours的store/update请求验证中加入
'approver_id' => [
    'required',
    Rule::exists('users', 'id'),
    function ($attribute, $value, $fail) {
        // 禁止审批自己的工时
        if ($value == auth()->id()) {
            $fail('不能审批自己提交的工时记录');
        }
        // 可选:验证审批人属于当前选中的Discipline
        $disciplineId = request()->input('discipline_id');
        if (!Discipline::find($disciplineId)->approvers()->where('user_id', $value)->exists()) {
            $fail('该审批人不属于所选的Discipline');
        }
    },
],

三、额外优化建议

给work_hours表添加status字段(如pending/approved/rejected),用于记录工时的审批状态,完善流程闭环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 23:05:22