moonvader
moonvader

Reputation: 21091

Delete related models in Laravel 6/7

There are many related questions but unfortunately I can't find working solution

I have Laravel model and when this model is deleted I want to

  1. delete some related models
  2. run custom SQL query when deleting model

My Laravel's model class looks like (as you can see models can have different relation types)

class ModelA extends Model
{
  public functions modelsB() {
    return $this->hasMany(ModelB:class);
  }

  public functions modelsC() {
    return $this->belongsToMany(ModelC:class);
  }

  // other related models
 
  // place where I am expecting actual deleting is happening
  public static function boot() {
        parent::boot();

        self::deleting(function($modelA) { 

          $modelA->modelsB()->get()->each->delete(); 
         
          foreach($modelA->modelsC as $modelC){
            $modelC->delete();
          }
    });
  }
}

ModelA is deleted but all related data stays, and I am not sure that it is even being called. Maybe I missed something? Should I extend some class for my ModelA? Or this boot function should be placed somewhere else?

Upvotes: 3

Views: 2625

Answers (5)

tsommie
tsommie

Reputation: 510

Did you cascade it in your migration file of your ModelB like so:

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

The `onDelete('cascade') is the important part, mind you this is a shorthand for:

$table->unsignedBigInteger('modelA_id');

$table->foreign('modelA_id')->references('id')
    ->on('modelA')->onDelete('cascade');

This should do most of the heavy lifting for you and you can find more info at Laravel Docs Migrations

If you don't like this approach and would prefer an event-based approach then try doing this in your ModelA class:

protected static function booted()
{
    static::deleted(function ($modelA) {
        $modelA->modelsB()->delete();
        // ...
    });
}

Upvotes: 3

mmabdelgawad
mmabdelgawad

Reputation: 2545

I would suggest using Observers to listen for ModelA deleted event.

  1. run php artisan make:observer ModelAObserver --model=ModelA
  2. in AppServiceProvider in boot() metohd add ModelA::observe(ModelAObserver::class);
  3. now in your ModelAObserver in deleted() method you will delete associated models and perform your custom SQL queries.
/**
 * Handle the ModelA "deleted" event.
 *
 * @param  \App\ModelA  $modelA
 * @return void
 */
public function deleted(ModelA $modelA)
{
    // delete associated models
    
    $modelA->modelsB()->delete();
    $modelA->modelsC()->delete()

    ... 

    // perform custom SQL queries

    ... 
}

you also, need to add a database transaction wherever you call delete on ModelA to make sure that action is completely done or not.

DB::transaction(function () use ($modelA) {
   $modelA->delete();
});

Upvotes: 2

Ankit Singh
Ankit Singh

Reputation: 922

If you are trying to delete the related model from within the model itself. You only need to call $this->reletedModel()->delete(); There is really no need of foreach() loop.

Given you have a function defining a relation with following declaration.

public function relatedModel()
{
    return $this->hasOne(RelatedModel::class);
    //OR
    //return $this->hasMany(RelatedModel::class);
}

/** Function to delete related models */
public function deleteRelatedModels()
{
    return $this->relatedModel()->delete();
}

Upvotes: 2

aleksejjj
aleksejjj

Reputation: 1835

What about https://laravel.com/docs/7.x/eloquent#events-using-closures? Something like

class ModelA extends Model
{
    public functions modelsB()
    {
        return $this->hasMany(ModelB::class);
    }

    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::deleted(function ($modelA) {
            $modelA->modelsB()->delete();
            // ...
        });
    }
}

Upvotes: 4

Emre Kaya
Emre Kaya

Reputation: 121

Did you try use static instead of self

In the documentation, Laravel recommends to using static keyword when binding event or observer

Upvotes: 2

Related Questions