Automatically deleting related rows in Laravel (Eloquent ORM)

后端 未结 13 1746
半阙折子戏
半阙折子戏 2020-11-22 17:03

When I delete a row using this syntax:

$user->delete();

Is there a way to attach a callback of sorts, so that it would e.g. do this auto

相关标签:
13条回答
  • 2020-11-22 17:35

    In my case it was pretty simple because my database tables are InnoDB with foreign keys with Cascade on Delete.

    So in this case if your photos table contains a foreign key reference for the user than all you have to do is to delete the hotel and the cleanup will be done by the Data Base, the data base will delete all the photos records from the data base.

    0 讨论(0)
  • 2020-11-22 17:35

    Or you can do this if you wanted, just another option:

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

    Note if you are not using the default laravel db connection then you need to do the following:

    DB::connection('connection_name')->pdo->beginTransaction();
    DB::connection('connection_name')->pdo->commit();
    DB::connection('connection_name')->pdo->rollBack();
    
    0 讨论(0)
  • 2020-11-22 17:38

    I would iterate through the collection detaching everything before deleting the object itself.

    here's an example:

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

    I know it is not automatic but it is very simple.

    Another simple approach would be to provide the model with a method. Like this:

    public function detach(){
           try {
                
                if ($this->has('photos')) {
                    foreach ($this->photos as $photo) {
        
                        $this->photos()->detach($photo);
                    }
                }
               
            } catch (Exception $e) {
                dd($e);
            }
    }
    

    Then you can simply call this where you need:

    $user->detach();
    $user->delete();
    
    0 讨论(0)
  • 2020-11-22 17:41

    To elaborate on the selected answer, if your relationships also have child relationships that must be deleted, you have to retrieve all child relationship records first, then call the delete() method so their delete events are fired properly as well.

    You can do this easily with 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();
            });
        }
    }
    

    You can also improve performance by querying only the relationships ID column:

    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();
            });
        }
    }
    
    0 讨论(0)
  • 2020-11-22 17:42

    It is better if you override the delete method for this. That way, you can incorporate DB transactions within the delete method itself. If you use the event way, you will have to cover your call of delete method with a DB transaction every time you call it.

    In your User model.

    public function delete()
    {
        \DB::beginTransaction();
    
         $this
            ->photo()
            ->delete()
        ;
    
        $result = parent::delete();
    
        \DB::commit();
    
        return $result;
    }
    
    0 讨论(0)
  • 2020-11-22 17:44

    yeah, but as @supersan stated upper in a comment, if you delete() on a QueryBuilder, the model event will not be fired, because we are not loading the model itself, then calling delete() on that model.

    The events are fired only if we use the delete function on a Model Instance.

    So, this beeing said:

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

    in order to delete the post tags when deleting the user, we would have to iterate over $user->posts and calling $post->delete()

    foreach($user->posts as $post) { $post->delete(); } -> this will fire the deleting event on Post

    VS

    $user->posts()->delete() -> this will not fire the deleting event on post because we do not actually load the Post Model (we only run a SQL like: DELETE * from posts where user_id = $user->id and thus, the Post model is not even loaded)

    0 讨论(0)
提交回复
热议问题