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

Laravel中单个模型相关记录的关联实现方法(类Jira/ADO关联)

Laravel 实现工单多对多关联(类似Jira/ADO工作项关联)

要实现工单彼此关联、查看任意工单即可获取所有关联记录的需求,最优方案是采用关联组+多对多中间表的模式——相比直接存储工单两两关联的冗余数据,这种方式更高效、灵活,还能支持工单加入多个关联组的场景。


一、表结构设计

1. 原有工单表(tickets)

假设你已有工单基础表,示例结构如下:

CREATE TABLE tickets (
    id INT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    content TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

2. 关联组表(ticket_association_groups)

用于存储关联组的元数据(可选名称,方便管理):

CREATE TABLE ticket_association_groups (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) COMMENT '关联组名称,如「用户反馈批量处理组」',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

3. 工单-关联组中间表(ticket_group_pivot)

实现工单与关联组的多对多关联,主键为联合键避免重复关联:

CREATE TABLE ticket_group_pivot (
    ticket_id INT NOT NULL,
    group_id INT NOT NULL,
    PRIMARY KEY (ticket_id, group_id),
    FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
    FOREIGN KEY (group_id) REFERENCES ticket_association_groups(id) ON DELETE CASCADE
);

优势说明:

  • 将工单1、2、3、4、9加入同一个关联组,即可实现彼此关联
  • 工单可同时属于多个关联组(比如工单1既和2、3关联,又和5、6关联)
  • 避免两两关联的冗余数据(5个工单仅需5条中间表记录,而非10条)

二、Laravel 模型实现

1. Ticket 模型(app/Models/Ticket.php)

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Ticket extends Model
{
    protected $fillable = ['title', 'content'];

    // 与关联组的多对多关系
    public function associationGroups(): BelongsToMany
    {
        return $this->belongsToMany(
            AssociationGroup::class,
            'ticket_group_pivot',
            'ticket_id',
            'group_id'
        );
    }

    // 自定义属性:获取当前工单的所有关联工单
    public function getAssociatedTicketsAttribute()
    {
        $groupIds = $this->associationGroups->pluck('id');

        return Ticket::whereHas('associationGroups', function ($query) use ($groupIds) {
            $query->whereIn('group_id', $groupIds);
        })
        ->where('id', '!=', $this->id)
        ->get();
    }
}

2. AssociationGroup 模型(app/Models/AssociationGroup.php)

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class AssociationGroup extends Model
{
    protected $table = 'ticket_association_groups';
    protected $fillable = ['name'];

    // 与工单的多对多关系
    public function tickets(): BelongsToMany
    {
        return $this->belongsToMany(
            Ticket::class,
            'ticket_group_pivot',
            'group_id',
            'ticket_id'
        );
    }
}

三、使用示例

1. 创建关联组并关联工单

// 创建关联组(可选名称)
$group = AssociationGroup::create(['name' => '用户反馈批量处理组']);

// 批量关联工单到组
$group->tickets()->attach([1, 2, 3, 4, 9]);

// 单个工单加入组
Ticket::find(1)->associationGroups()->attach($group->id);

2. 获取工单的所有关联记录

$ticket = Ticket::find(1);
// 直接访问自定义属性拿到所有关联工单
$associatedTickets = $ticket->associatedTickets;

// 遍历输出关联工单ID
foreach ($associatedTickets as $relatedTicket) {
    echo $relatedTicket->id; // 输出 2,3,4,9
}

3. 移除关联

// 从组中移除单个工单
$group->tickets()->detach(1);

// 移除工单的所有关联组
Ticket::find(1)->associationGroups()->detach();

四、优化建议

  • 给ticket_group_pivot添加(group_id, ticket_id)索引,提升关联查询速度
  • 若需区分关联类型(如「关联」「依赖」「阻塞」),可在ticket_group_pivot中添加type字段(ENUM('related','depends','blocks')),并在模型查询时过滤类型
  • 若不需要关联组名称,可简化结构:移除ticket_association_groups表,直接用group_uuid字段存储在tickets中,但灵活性会降低

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 09:07:40