Search code examples
phplaravellaravel-11laravel-eventslaravel-models

How to handle eloquent.deleted Event for all Models right?


I want to run an action after any model got deleted, but currently it seems like I do something wrong.. I already get the Model before I delete it, so the event should get fired.

As I do already some general handling in my AppServiceProvider and it's only a small piece of code I want to handle the Event direct in the Provider.

simplified AppServiceProvider.php

<?php

namespace App\Providers;

use App\Models\DebugLog;
use App\Models\Jobs\JobLog;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Event;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Event::listen('eloquent.deleted: *', function(string $eventName, array $data){
            DebugLog::create([
                'function' => 'AppServiceProvider: Event eloquent.deleted',
                'object' => json_encode($data,JSON_PRETTY_PRINT),
                'message' => $eventName
            ]);
            //JobLog::where('tablename',$c_table)->where('entryid',$c_id)->delete();
        });
    }
}

The Model get deleted like this

$deletionSucc = InboundPlan::where('inboundplanid',$container->inboundplanid)->where('account',$container->__get('account'))->first()->delete();

So normally the event should get fired that way and the listener looks pretty much like here: https://laravel.com/docs/11.x/events#wildcard-event-listeners

Laravel logfile doesn't show any error, too. May do I the whole listening wrong? That's why I wrote the title like it is.

EDIT (my solution):

I decided to use a Trait for this case, because in the end NOT EVERY Model should trigger the code. In the beginning I just don't want to add code to every related Model. The Trait looks like this:

JobLogDeletionTrait.php

<?php

namespace App\Traits;

use App\Models\DebugLog;
use App\Models\Jobs\JobLog;

trait JobLogDeletionTrait
{
    public static function bootJobLogDeletionTrait()     
    {         
        static::deleting(function ($model) {
            $modelArr = $model->toArray();
            $succ = JobLog::where('tablename',$model->getTable())->where('entryid',$modelArr[$model->getKeyName()])->delete();
        });          
        
        static::deleted(function ($model) {        
            $modelArr = $model->toArray();     
            $succ = JobLog::where('tablename',$model->getTable())->where('entryid',$modelArr[$model->getKeyName()])->delete();
        });     
    } 
}

Inside the related Model you only need to use the Trait, what is imo a cleaner way as to listen to every model deletion event. This link helped me at the decision making: https://satyanveshak.com/articles/handle-events-for-eloquent-models-in-laravel/#3-alternative-using-traits


Solution

  • You can use directly Model to listen the events in AppServiceProvider.

    for example:

    // Listen for the "deleting" event on all models         
        Model::deleting(function ($model) {             
            Log::info('A model is about to be deleted:', [                
                'model' => get_class($model),                 
                'attributes' => $model->toArray(),             
            ]);         
        });  
    
        // Listen for the "deleted" event on all models         
        Model::deleted(function ($model) {            
            Log::info('A model has been deleted:', [                
                'model' => get_class($model),                 
                'attributes' => $model->toArray(),            
            ]);        
        });     
    

    You just need to take care of actions based on Model class and which models to be excluded.

    this might help Listen Events on all elequent models globally