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

Laravel 8 Eloquent设置cascadeOnDelete报错(SQL Server 2018)

解决SQL Server中Laravel多外键级联删除的循环路径错误

SQL Server 2018不允许同一张子表(budgets)有多个外键指向同一个父表(users)并设置ON DELETE CASCADE,因为这会产生多重级联路径,数据库无法确定级联操作的执行顺序,因此抛出约束创建失败的错误。以下是两种可行的解决方法:

方案一:使用Laravel模型事件手动处理级联删除

通过Laravel的模型生命周期事件实现业务逻辑,避开数据库层面的限制,同时保持代码的可维护性。

1. 修改budgets表迁移,移除级联删除设置

将外键的cascadeOnDelete()替换为nullOnDelete()或直接移除(默认行为为NO ACTION):

public function up()
{
    Schema::create('budgets', function (Blueprint $table) {
        $table->id();

        $table->foreignId('requestor_id')->nullable()
            ->references('id')->on('users')
            ->nullOnDelete();

        $table->foreignId('approver_id')->nullable()
            ->references('id')->on('users')
            ->nullOnDelete();

        $table->timestamps();
    });
}

2. 在User模型中添加删除事件处理

在app/Models/User.php中注册deleting事件,删除用户前自动清理关联的预算记录:

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable;

    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    protected static function boot()
    {
        parent::boot();

        static::deleting(function ($user) {
            // 删除该用户作为申请人的所有预算
            $user->requestedBudgets()->delete();
            // 删除该用户作为审批人的所有预算
            $user->approvedBudgets()->delete();
        });
    }

    // 定义与预算表的关联关系
    public function requestedBudgets()
    {
        return $this->hasMany(Budget::class, 'requestor_id');
    }

    public function approvedBudgets()
    {
        return $this->hasMany(Budget::class, 'approver_id');
    }
}

3. 创建Budget模型

确保app/Models/Budget.php存在:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Budget extends Model
{
    protected $fillable = ['requestor_id', 'approver_id'];
}

方案二:使用SQL Server触发器实现级联删除

通过数据库触发器替代外键级联逻辑,直接在数据库层面处理删除操作。

1. 修改budgets表迁移并添加触发器

移除外键的级联设置,同时在迁移中创建SQL Server触发器:

public function up()
{
    Schema::create('budgets', function (Blueprint $table) {
        $table->id();

        $table->foreignId('requestor_id')->nullable()
            ->references('id')->on('users');

        $table->foreignId('approver_id')->nullable()
            ->references('id')->on('users');

        $table->timestamps();
    });

    // 创建触发器
    DB::unprepared('
        CREATE TRIGGER DeleteUserBudgets ON users
        AFTER DELETE
        AS
        BEGIN
            SET NOCOUNT ON;
            DELETE FROM budgets
            WHERE requestor_id IN (SELECT id FROM deleted)
            OR approver_id IN (SELECT id FROM deleted);
        END
    ');
}

public function down()
{
    // 回滚时删除触发器
    DB::unprepared('DROP TRIGGER IF EXISTS DeleteUserBudgets');
    Schema::dropIfExists('budgets');
}

触发器会在users表记录被删除后,自动清理budgets表中所有关联该用户的记录,实现预期的级联效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 07:25:27