在Laravel中自动删除相关行(Eloquent ORM)

Mar*_*ine 133 php laravel

当我使用以下语法删除行时:

$user->delete();
Run Code Online (Sandbox Code Playgroud)

有没有办法附加各种回调,所以它会自动执行此操作:

$this->photo()->delete();
Run Code Online (Sandbox Code Playgroud)

最好在模型类内.

Chr*_*itz 180

您实际上可以在迁移中进行设置:

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

资料来源:http://laravel.com/docs/5.1/migrations#foreign-key-constraints

您还可以为约束的"on delete"和"on update"属性指定所需的操作:

$table->foreign('user_id')
      ->references('id')->on('users')
      ->onDelete('cascade');
Run Code Online (Sandbox Code Playgroud)

  • 但是如果你使用的是软删除,那就不行了,因为这些行并没有被真正删除. (58认同)
  • 这种方法依赖于DB来进行级联删除,但并非所有数据库都支持这种方法,因此需要格外小心.例如,带有MyISAM引擎的MySQL没有,也没有任何NoSQL DB,默认设置中的SQLite等.另外一个问题是,当您运行迁移时,工匠不会向您发出警告,它不会在MyISAM表上创建外键和当您稍后删除记录时,不会发生级联.我曾经遇到过这个问题而且相信我很难调试. (8认同)
  • 此外 - 这将删除数据库中的记录,但不会运行您的删除方法,因此如果您正在进行删除的额外工作(例如 - 删除文件),它将不会运行 (5认同)
  • @kehinde您显示的方法不会在要删除的关系上调用删除事件。您应该迭代关系并单独调用删除。 (2认同)

iva*_*hoe 178

我相信这是Eloquent事件的完美用例(http://laravel.com/docs/eloquent#model-events).您可以使用"删除"事件进行清理:

class User extends Eloquent
{
    public function photos()
    {
        return $this->has_many('Photo');
    }

    // this is a recommended way to declare event handlers
    public static function boot() {
        parent::boot();

        static::deleting(function($user) { // before delete() method call this
             $user->photos()->delete();
             // do the rest of the cleanup...
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

您可能还应该将整个事情放在事务中,以确保参照完整性.

  • 但这并没有进一步级联.例如,如果`Photos`有`tags`并且你在`Photos`模型中做同样的事情(即``delete`方法:`$ photo-> tags() - > delete();`)它永远不会被触发.但是如果我把它作为`for`循环并做一些像'for($ user-> photos as $ photo){$ photo-> delete(); 那么`标签'也会被删除!仅供参考 (7认同)
  • 注意:我花了一些时间才开始工作.我需要在查询中添加`first()`以便我可以访问*model-event*例如`User :: where('id','=',$ id) - > first() - > delete() ;`[来源](http://laravel.io/forum/11-08-2014-eloquent-deleting-model-event-doesnt-fire#reply-16665) (6认同)
  • @MichelAyres:是的,你需要在模型实例上调用delete(),而不是在Query Builder上调用.Builder有它自己的delete()方法,它基本上只运行一个DELETE sql查询,所以我认为它对orm事件一无所知...... (5认同)
  • 几乎在Laravel 5.5中工作,我不得不添加一个`foreach($ user->照片作为$ photo)`,然后`$ photo-> delete()`以确保每个孩子在所有级别上删除其子项,而不是因为某种原因,它只发生了一次. (4认同)
  • 这是软删除的方法.我相信新的/首选的Laravel方法是以这种方式将所有这些粘贴在AppServiceProvider的boot()方法中:\ App\User :: deletion(function($ u){$ u-> photos() - > delete( );}); (3认同)

akh*_*khy 48

注意:这个答案是为Laravel 3编写的.因此,在更新版本的Laravel中可能会或可能不会很好.

您可以在实际删除用户之前删除所有相关照片.

<?php

class User extends Eloquent
{

    public function photos()
    {
        return $this->has_many('Photo');
    }

    public function delete()
    {
        // delete all related photos 
        $this->photos()->delete();
        // as suggested by Dirk in comment,
        // it's an uglier alternative, but faster
        // Photo::where("user_id", $this->id)->delete()

        // delete the user
        return parent::delete();
    }
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你.

  • 为了提高效率,请使用一个查询:Photo :: where("user_id",$ this-> id) - > delete(); 不是最好的方式,但只有1个查询,*方式*如果用户有1.000.000照片的更好的性能. (20认同)
  • 实际上你可以调用:$ this-> photos() - > delete(); 不需要循环 - 艾芬豪 (5认同)
  • @ivanhoe我注意到如果删除了集合,删除事件将不会在照片中触发,但是,如果akhyar建议迭代将导致删除事件触发.这是一个错误吗? (4认同)

Cal*_*aga 25

用户模型中的关系:

public function photos()
{
    return $this->hasMany('Photo');
}
Run Code Online (Sandbox Code Playgroud)

删除记录和相关:

$user = User::find($id);

// delete related   
$user->photos()->delete();

$user->delete();
Run Code Online (Sandbox Code Playgroud)

  • 这是可行的,但是如果涉及到一个piviot表(在hasMany / belongsToMany关系的情况下),请小心使用$ user()-&gt; relation()-&gt; detach(),否则您将删除引用而不是该关系。 (3认同)

Att*_*lop 14

从Laravel 5.2开始,文档指出应该在AppServiceProvider中注册这些类型的事件处理程序:

<?php
class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::deleting(function ($user) {
            $user->photos()->delete();
        });
    }
Run Code Online (Sandbox Code Playgroud)

我甚至假设将它们移到单独的类而不是闭包中以获得更好的应用程序结构.

  • 如果你有'hasMany'关系_from_你的`photos()`,你还需要小心 - 这个过程将*不*删除孙子,因为你没有加载模型.你需要循环"照片"(注意,而不是`photos()`)并在它们上面激活`delete()`方法作为模型,以便触发与删除相关的事件. (3认同)
  • Laravel 5.3 建议将它们放在名为 [Observers](https://laravel.com/docs/5.3/eloquent#observers) 的单独类中——虽然它只在 5.3 中记录,但`Eloquent::observe()` 方法可用在 5.2 中也是如此,可以从 AppServiceProvider 中使用。 (2认同)
  • @Leith 观察方法在 5.1 中也可用。 (2认同)

Par*_*ras 14

解决这个问题有3种方法:

1.在模型启动时使用Eloquent事件(参考:https://laravel.com/docs/5.7/eloquent#events)

class User extends Eloquent
{
    public static function boot() {
        parent::boot();

        static::deleting(function($user) {
             $user->photos()->delete();
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

2.使用Eloquent事件观察员(参考:https://laravel.com/docs/5.7/eloquent#observers)

在AppServiceProvider中,像这样注册观察者:

public function boot()
{
    User::observe(UserObserver::class);
}
Run Code Online (Sandbox Code Playgroud)

接下来,添加一个Observer类,如下所示:

class UserObserver
{
    public function deleting(User $user)
    {
         $user->photos()->delete();
    }
}
Run Code Online (Sandbox Code Playgroud)

3.使用外键约束(参考:https://laravel.com/docs/5.7/migrations#foreign-key-constraints)

$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Run Code Online (Sandbox Code Playgroud)

  • 我认为这 3 个选项是最优雅的,因为将约束构建到数据库本身中。我测试了一下,效果很好。 (2认同)

BAB*_*AFI 9

使用Constrained()

Laravel 7之后,新的foreignId()constrained()方法可用于定义数据库中的关系约束。OnDelete()可以在这些方法上使用方法来自动删除相关记录。

老式

$table->unsignedBigInterer('user_id');

$table->foreign('user_id')
    ->references('id')
    ->on('users')
    ->onDelete('cascade');
Run Code Online (Sandbox Code Playgroud)

新风格

$table->foreignId('user_id')
      ->constrained()
      ->onDelete('cascade');
Run Code Online (Sandbox Code Playgroud)


Ste*_*man 6

为了详细说明所选答案,如果您的关系还具有必须删除的子关系,则必须首先检索所有子关系记录,然后调用该方法,delete()以便也正确触发它们的删除事件。

您可以使用更高阶的消息轻松地做到这一点。

class User extends Eloquent
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    public static function boot() {
        parent::boot();

        static::deleting(function($user) {
             $user->photos()->get()->each->delete();
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

您还可以通过仅查询关系 ID 列来提高性能:

class User extends Eloquent
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    public static function boot() {
        parent::boot();

        static::deleting(function($user) {
             $user->photos()->get(['id'])->each->delete();
        });
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 6

最好delete为此重写该方法。这样,您就可以将 DB 事务合并到delete方法本身中。如果您使用事件方式,则delete每次调用它时都必须用数据库事务覆盖您的方法调用。

在你的User模型中。

public function delete()
{
    \DB::beginTransaction();

     $this
        ->photo()
        ->delete()
    ;

    $result = parent::delete();

    \DB::commit();

    return $result;
}
Run Code Online (Sandbox Code Playgroud)