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

Posted

技术标签:

【中文标题】在 Laravel 5.4 (Eloquent ORM) 中自动删除 NESTED 相关行【英文标题】:Automatically deleting NESTED related rows in Laravel 5.4 (Eloquent ORM) 【发布时间】:2017-12-17 22:53:37 【问题描述】:

假设我有一本书。本书有章节,本书中的那些章节有子章节。

所以我有三个模型:

书籍 > 章节 > 子章节

当我删除这本书时($book->delete();),我还想删除这本书的相关章节以及书中所有章节的相关子章节。

在这里 (Automatically deleting related rows in Laravel (Eloquent ORM)) 我发现了 Eloquent Events。每当一本书被删除时,在此之前,章节就会被删除,因为我们挂钩:

class Book extends Eloquent

    public function chapters()
    
        return $this->has_many('Chapter');
    

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

        static::deleting(function($book)  
             $book->chapters()->delete();
        );
    

所以我想,我只需要在我的 Chapter-Model 中实现相同的代码,只需将“Book”与“Chapter”交换,“Chapter”与“Subchapter”交换:

class Chapter extends Eloquent

    public function subChapters()
    
        return $this->has_many('SubChapter');
    

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

        static::deleting(function($chapter)  
             $chapter->subChapters()->delete();
        );
    

当我删除章节时,这很好用。所有子章节也将被删除。

当我删除这本书时,它适用于章节。所有章节也将被删除。

但是,当我删除这本书时,它只会删除章节。它不会删除已删除章节的相关子章节。

谁能帮帮我?

【问题讨论】:

在 RDBMS 级别上做。 尝试使用迁移选项(级联删除) 尝试循环遍历章节并一一删除foreach ($book->chapters()->get() as $chapter) $chapter->delete(); 和子章节中的相同foreach ( $chapter->subChapters()->get() as $subChapter) $subChapter->delete(); 【参考方案1】:

那是因为当你同时删除多个对象时,并不会触发每个模型的启动删除功能,所以你应该循环遍历对象并一个一个地删除它们:

class Book extends Eloquent

  public function chapters()
   
    return $this->has_many(Chapter::class);
   

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

    static::deleting(function($book)  
        foreach($book->chapters as $chapter)
          $chapter->delete();
        
    );
  

/********************************/
 class Chapter extends Eloquent

  public function subChapters()
   
    return $this->has_many(SubChapter::class);
   

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

    static::deleting(function($chapter)  
        foreach($chapter->subChapters as $subChapter)
          $subChapter->delete();
        
    );
  

但是我的建议是在表之间设置级联外键关系,以便 DBMS 自动删除相关行,下面的示例代码向您展示了如何在迁移文件中执行此操作:

 Schema::create('chapters', function (Blueprint $table) 
        $table->increments('id');
        $table->integer('book_id')->unsigned();
        $table->string('title');
        $table->timestamps();


        $table->foreign('book_id')
        ->references('id')
        ->on('books')
        ->onDelete('cascade')
        ->onUpdate('cascade');  
    );

对子章节执行相同操作。 希望对您有所帮助...

【讨论】:

谢谢,托希德。重要提示:不要忘记“->unsigned()”!【参考方案2】:

这是因为$book->chapters() 只会返回Builder 的一个实例。当您在构建器上调用delete() 时,它只会运行删除这些模型所需的查询,而不是新建它们然后删除它们,这意味着Chapter 上的deleting 事件将永远不会被触发。

要解决这个问题,您可以执行以下操作:

protected static function boot() 

    parent::boot();

    static::deleting(function($book)  

        $book->chapters->each(function ($chapter) 
            $chapter->delete();
        );

    );

希望这会有所帮助!

【讨论】:

谢谢,罗斯。这是有道理的。在此处阅读更多信息:laravel-news.com/higher-order-messaging。尽管如此,它对我不起作用:未定义的属性:Illuminate\Database\Eloquent\Relations\HasMany::$each 似乎无法识别“each->”。我是不是忘记了什么? @andydotlutz 啊,很公平。在这种情况下,我将只使用普通的each() 方法。我已经编辑了我的答案以反映这一点。 @Muhammad 在 OP 的问题中,Chapter 模型也有一个 deleting 钩子。删除相关模型的方法取决于他们是想简单地从数据库中删除行还是需要挂钩 Eloquent 事件。【参考方案3】:

正确的做法是使用onDelete('cascade')。在chapters表迁移:

$table->foreign('book_id')
      ->references('id')
      ->on('books')
      ->onDelete('cascade');

subchapters 迁移中:

$table->foreign('chapter_id')
      ->references('id')
      ->on('chapters')
      ->onDelete('cascade');

在这种情况下,您无需编写任何代码,所有章节和子章节都会自动删除。

【讨论】:

以上是关于在 Laravel 5.4 (Eloquent ORM) 中自动删除 NESTED 相关行的主要内容,如果未能解决你的问题,请参考以下文章

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

使用 Laravel 5.4 播种功能时出错

Laravel 5.4:使用eloquent和fillable字段更新多个表的多个模型

Laravel 5.4雄辩的一对多关系

Laravel Eloquent multiple whereHas on relationship where column 'order' 高于前一个 whereHas

如何在 Laravel/Eloquent 中获得完整的关系