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

时间:2013-01-05 16:55:29

标签: php laravel

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

$user->delete();

是否有办法附加各种类型的回调,以便例如自动执行此操作:

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

最好在模型类中。

13 个答案:

答案 0 :(得分:171)

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

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

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

  

您也可以为“on delete”和“on”指定所需的操作   更新“约束的属性:

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

答案 1 :(得分:164)

我相信这是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...
        });
    }
}

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

答案 2 :(得分:45)

  

注意:这个答案是为 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();
    }
}

希望它有所帮助。

答案 3 :(得分:22)

用户模型中的关系:

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

删除记录及相关内容:

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

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

$user->delete();

答案 4 :(得分:12)

从Laravel 5.2开始,应在AppServiceProvider中注册这些事件处理程序的documentation states

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

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

答案 5 :(得分:2)

使用 Constrained()

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

老式

$table->unsignedBigInterer('user_id');

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

新风格

$table->foreignId('user_id')
      ->constrained()
      ->onDelete('cascade');

答案 6 :(得分:1)

在我的情况下,它非常简单,因为我的数据库表是InnoDB,其中包含带有Cascade on Delete的外键。

因此,在这种情况下,如果您的照片表包含用户的外键引用,那么您要做的就是删除酒店,清理将由数据库完成,数据库将删除所有照片记录来自数据库。

答案 7 :(得分:1)

在删除对象本身之前,我会遍历集合中的所有内容。

这是一个例子:

try {
        $user = user::findOrFail($id);
        if ($user->has('photos')) {
            foreach ($user->photos as $photo) {

                $user->photos()->detach($photo);
            }
        }
        $user->delete();
        return 'User deleted';
    } catch (Exception $e) {
        dd($e);
    }

我知道这不是自动的,但很简单。

另一种简单的方法是为模型提供方法。像这样:

public function detach(){
       try {

            if ($this->has('photos')) {
                foreach ($this->photos as $photo) {

                    $this->photos()->detach($photo);
                }
            }

        } catch (Exception $e) {
            dd($e);
        }
}

然后,您只需在需要的地方拨打电话:

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

答案 8 :(得分:1)

最好为此覆盖sizeOfServiceInstanceRow方法。这样,您可以在delete方法本身中合并数据库事务。如果使用事件方式,则每次调用它时,都必须使用数据库事务来覆盖对delete方法的调用。

在您的delete模型中。

User

答案 9 :(得分:0)

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

您可以使用higher order messages轻松地做到这一点。

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();
        });
    }
}

您还可以通过仅查询“关系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();
        });
    }
}

答案 10 :(得分:0)

您可以使用此方法作为替代。

将会发生的是,我们将所有与用户表关联的表都使用循环并删除相关数据

$tables = DB::select("
    SELECT
        TABLE_NAME,
        COLUMN_NAME,
        CONSTRAINT_NAME,
        REFERENCED_TABLE_NAME,
        REFERENCED_COLUMN_NAME
    FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
    WHERE REFERENCED_TABLE_NAME = 'users'
");

foreach($tables as $table){
    $table_name =  $table->TABLE_NAME;
    $column_name = $table->COLUMN_NAME;

    DB::delete("delete from $table_name where $column_name = ?", [$id]);
}

答案 11 :(得分:-1)

或者你可以这样做,只是另一种选择:

try {
    DB::connection()->pdo->beginTransaction();

    $photos = Photo::where('user_id', '=', $user_id)->delete(); // Delete all photos for user
    $user = Geofence::where('id', '=', $user_id)->delete(); // Delete users

    DB::connection()->pdo->commit();

}catch(\Laravel\Database\Exception $e) {
    DB::connection()->pdo->rollBack();
    Log::exception($e);
}

注意,如果您没有使用默认的laravel db连接,则需要执行以下操作:

DB::connection('connection_name')->pdo->beginTransaction();
DB::connection('connection_name')->pdo->commit();
DB::connection('connection_name')->pdo->rollBack();

答案 12 :(得分:-1)

是的,但是正如@supersan在评论的上方指出的那样,如果您在QueryBuilder上删除(),则不会触发模型事件,因为我们没有加载模型本身,然后在该模型上调用delete()。

仅当我们在模型实例上使用delete函数时,才会触发事件。

所以,这只蜜蜂说:

if user->hasMany(post)
and if post->hasMany(tags)

为了在删除用户时删除帖子标签,我们必须遍历$user->posts并调用$post->delete()

foreach($user->posts as $post) { $post->delete(); }->这将在Post上触发删除事件

VS

{$user->posts()->delete()->这不会在发布时触发Delete事件,因为我们实际上并未加载Post模型(我们仅运行类似DELETE * from posts where user_id = $user->id的SQL,因此Post模型甚至没有已加载)