如何在Laravel4中进行软删除级联?

12

尝试使用带有删除级联和软删除的外键,但并没有太大的成功。

我有两个表:Users(用户)和Events(事件)。这两个表都有软删除功能。

一个用户可以拥有0到n个事件。
事件有一个user_id,作为在用户表上的外键,就像这样:

$table->foreign('user_id')->references('id')->on('users')->onDelete('CASCADE')->onUpdate('CASCADE');

问题是,当我删除一个用户时,它会被软删除,但它的事件不会-无论是软删除还是物理删除。

我做错了什么吗?还是这是正确的Eloquent行为?

其次,如果这是正确的行为,如何最好地实现删除级联?也许像这样覆盖我的模型中的delete()方法...

public function delete()
{
  //delete all events...
  __parent::delete()
}

?

3个回答

11

Cryode,我尝试了模型事件,它可以工作,但只是部分地。让我解释一下。实际情况是有3个表:用户、事件和评论。事件有0到n个评论。根据链接的建议,我在Users和Events的boot()方法中添加了逻辑。 现在:软删除事件,预期:软删除评论,结果:OK; 软删除用户,预期:软删除事件,结果:OK,软删除这些事件的评论,结果:KO。似乎删除不会传播到第一个“级别”之外。 - RedSash
1
这是我自己实现的解决方案。我首先绘制了一个地图,展示了我的所有模型之间的关系,然后编写了删除方法来扩展优雅模型中的模型,以便进行级联删除。最后,由于这是一个容易出错的任务,我编写单元测试来显示级联效应只影响它应该影响的内容。这样,用户删除调用UserRecords Delete,然后调用UserRecordsMeta delete等等。 - carbontwelve

2

您在想得太多了。

要么就在删除用户之前直接删除事件:

$user->events()->delete();
$user->delete();

在用户模型中创建一个客户删除函数:
public function customDelete(){
    $this->events()->delete();
    return $this->delete();
}

您还可以添加模型观察者并监视删除或删除事件,但在您上述提到的场景中,前两种方法将是更简单的解决方案。

http://laravel.com/docs/4.2/eloquent#model-observers


0

如果我理解正确,您正在尝试在两个表中级联软删除?

我认为使用ON UPDATE CASCADE不是正确的方法。我会尝试解释一下原因...

要尝试这样做,您需要创建外键到复合键的关系。

也就是说,您需要将(events.user_id和deleted_at)链接到(user.id和delete_at)。更改其中一个,它将更新另一个。

首先,您需要向已删除的列添加默认规则,因为您无法链接空值。

因此,在两个表的迁移中添加以下内容...

$table->softDeletes()->default('0000-00-00 00:00:00');

在用户表中使用'id'和'deleted_at'添加唯一键

Schema::table('users; function($table) { $table->unique(array('id','deleted_at')) });

然后在事件表中创建一个外键,如下所示(链接到唯一键)

Schema::table('events', function($table) { $table->foreign(array('user_id','deleted_at'),'events_deleted_at_foreign_key')-> }->references(array('id','deleted_at'))->on('users')->onUpdate('CASCADE'));

运行此代码,您现在应该发现如果您软删除用户,则会软删除其事件。

但是,如果您现在尝试软删除事件,则会在外键约束上失败。你可能会问为什么!?

好吧,您正在使用两个表中的id、deleted_at创建父子关系。更新父项将更新子项。关系不会断开。但是,如果您更新子项,则关系现在已经断开,使子项成为表中的孤儿。这将导致外键约束失败。

这是一个冗长的回答,但希望能够清楚地解释为什么你试图使用ON UPDATE CASCADE来实现的方法行不通,并且节省你大量的时间。要么进入TRIGGERS并触发一个函数来处理你想要做的事情,要么在你的应用程序中处理它。个人建议使用TRIGGERS,这样数据库仍然是自己的实体,不需要依赖任何东西来保持数据完整性。

delimiter //

创建触发器 soft_delete_child,当 db.users 表中的记录更新时,针对每一行执行以下操作:

BEGIN

IF NEW.deleted_at <> OLD.deleted_at THEN

UPDATE events SET deleted_at=NEW.deleted_at WHERE events.user_id=NEW.id;

END IF;

END;

//

注意:分隔符为 ;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接