194

当我使用此语法删除一行时:

$user->delete();

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

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

最好在模型类内部。

4

17 回答 17

266

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

您可能还应该将整个事物放入事务中,以确保引用完整性..

于 2013-11-20T22:05:56.767 回答
232

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

$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');
于 2013-01-05T20:56:24.840 回答
62

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

希望能帮助到你。

于 2013-01-05T17:23:47.043 回答
40

用户模型中的关系:

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

删除记录及相关:

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

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

$user->delete();
于 2015-01-06T19:13:15.660 回答
30

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

2. 使用 Eloquent Event Observers(参考:https ://laravel.com/docs/5.7/eloquent#observers )

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

public function boot()
{
    User::observe(UserObserver::class);
}

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

class UserObserver
{
    public function deleting(User $user)
    {
         $user->photos()->delete();
    }
}

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

$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
于 2018-09-14T23:02:37.810 回答
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();
        });
    }

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

于 2016-05-18T18:15:52.013 回答
8

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

在你的User模型中。

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

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

    $result = parent::delete();

    \DB::commit();

    return $result;
}
于 2019-07-04T05:48:11.747 回答
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();
        });
    }
}

您还可以通过仅查询关系 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();
        });
    }
}
于 2019-01-16T20:06:58.810 回答
4

使用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');
于 2021-05-28T08:53:49.820 回答
2

在删除对象本身之前,我将遍历集合以分离所有内容。

这是一个例子:

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();
于 2018-02-21T20:40:30.863 回答
1

就我而言,这非常简单,因为我的数据库表是带有外键的 InnoDB,删除时带有 Cascade。

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

于 2015-01-07T08:11:55.420 回答
1

这种方式在 Laravel 8 上对我有用:

public static function boot() {

    parent::boot();
    
    static::deleted(function($item){
        $item->deleted_by = \Auth::id(); // to know who delete item, you can delete this row
        $item->save();  // to know who delete item, you can delete this row
        foreach ($item->photos as $photo){
            $photo->delete();
        }
    });
}

public function photos()
{
    return $this->hasMany('App\Models\Photos');
}

注意:以这种语法删除$user->photos()->delete();对我不起作用......

于 2021-11-30T07:28:58.587 回答
0

或者您可以根据需要执行此操作,只是另一种选择:

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();
于 2013-02-22T07:47:30.877 回答
0

这里有完美的解决方案。

# model

public function order_item_properties()
{
    return $this->hasMany(OrderItemProperty::class, 'order_id', 'id');
}

public function order_variations()
{
    return $this->hasMany(OrderItemVariation::class, 'order_id', 'id');
}

# controller

$order_item = OrderItem::find($request->order_id);

$order_item->order_item_properties()->delete();
$order_item->order_variations()->delete();

$order_item->delete();

return response()->json([
    'message' => 'Deleted',
]);
于 2022-02-16T04:16:35.623 回答
0

在定义模型的迁移时最好使用 onDelete 级联。这会为您删除模型的关系:

例如

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

如果您碰巧发现自己正在考虑如何将模型及其关系删除到大于 3 或 4 个嵌套关系的级别,那么您应该考虑重新定义模型的关系。

于 2021-11-30T08:16:22.510 回答
-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()-> 这不会触发 post 上的删除事件,因为我们实际上并没有加载 Post 模型(我们只运行类似的 SQL:DELETE * from posts where user_id = $user->id因此,甚至没有加载 Post 模型)

于 2018-09-24T12:38:58.743 回答
-2

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

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

$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]);
}
于 2019-04-17T10:53:05.297 回答