Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Guardian's avatar

Hook into Model events?

Dear All,

I am working on an application that has some models (obviously). Now I want to be able to hook into the default events provided by Laravel.

When looking at Model.php this should be

    public function getObservableEvents()
    {
        return array_merge(
            [
                'creating', 'created', 'updating', 'updated',
                'deleting', 'deleted', 'saving', 'saved',
                'restoring', 'restored',
            ],
            $this->observables
        );
    }

I have been googling around for this and have found 2 approaches.

I would like to get some insights / opinions on this.

Many thanks in advance.

Ps: I am running L 5.2

0 likes
7 replies
Guardian's avatar

@martinbean ,

Omg, I cant believe I missed that?! I was reading through the 5.2 docu all the time and couldn't not find a clean example there.

Here is what I have right now (big thanks to you pointing me to the right direction :) )

    public function boot()
    {
        \App\User::updated(function($user) {
            $log = new \App\Log([
              'type' => 'debug',
              'message' => 'debug message'
            ]);
            $user->log($log);
        });
    }

As you might have guessed I am logging the fact that the crud operation was performed.

Now I was a little bit lost with Events & Handlers...

Is there a "cleaner" way of putting my logging logic into a handler of some kind?

As you might have guessed, new to Events here.

Thanks again!

Guardian's avatar
Guardian
OP
Best Answer
Level 1

Dear all,

After some playing around with all of this, here is how I implemented it for now.

I wanted to have an event that was fairly flexible, since I want to be able to log CRUD operations on various models, but I also want to be able to fire the log event in my controllers or elsewhere

I added this to the App\Providers\EventServiceProvider to register my event & listener

    protected $listen = [
        'App\Events\LogEvent' => [
            'App\Listeners\LogListener',
        ],
    ];

I created my App\Events\LogEvent & App\Listeners\LogListener (php artisan make:listener LogListener --event=LogEvent) For now LogEvent only contains this, to be elaborated with referer, currently logged in user etc

    public function __construct($message)
    {
        $this->message = $message;
    }

LogListener

    public function handle(LogEvent $event)
    {
        $log = new Log([
          'type' => 'debug',
          'message' => $event->message,
        ]);
        $log->user_id = Sentinel::check() ? Sentinel::check()->id : null;
        $log->save();
    }

I created App\Providers\CrudLoggerProvider where I hooked into the "saving" event on the model, to log each time a model was created / updated and implemented some additional logic to validate if the user was created or updated

        User::saving(function(User $user) {
            $message = '';
            foreach($user->getDirty() as $attribute => $value){
                $original= $user->getOriginal($attribute);
                if ($original != '') {
                    $message .= "Changed $attribute from '$original' to '$value'<br/>\r\n";
                }
            }
            if($message == '') {
                $message = 'User '.$user->last_name.' '.$user->first_name.' was created';
            }
            event(new LogEvent($message));
        });

Finally, register your service provider in the 'providers' array in config/app.php like so

        App\Providers\CrudLoggerProvider::class,

Just as an additional resource, I have implemented the basic logging in one of my controllers that sends a password reset link to the user like so

event(new LogEvent('Password reset requested for ' .$user->email));

I am very interested to gather some feedback or suggestions on improving this and I hope that this can serve as a resource to others.

Many thanks in advance.

SaeedPrez's avatar

@Guardian

I wish more people on this forum were like you. A simple nudge in the right direction and you did all the hard work, you tried instead of asking other people to write the code for you or asking "Does this work?" and found a good solution which you came back to share.

Kudos to you.

4 likes
Guardian's avatar

Thanks for the nice comments guys!

Happy to do my part. :)

juniorjs's avatar

@Guardian , thanks a lot. This saved my day, as I'm yet starting with Laravel.

In appreciation, follow some modification I've made to make it suitable to my needs and, maybe, can help another lost soul as myself:

LogEvent class

public function __construct( $table, $message )
    {
        $this->table = $table;

        $this->message = $message;
    }   

LogListener

 public function handle(LogEvent $event)
    {
        $log = new Log;

        $log->setAttribute('id_user', session('user.id_user') );

        $log->setAttribute('nam_table', $event->table);

        $log->setAttribute('dsc_log', $event->message );

        $log->save();
    }

In CrudLoggerProvider, I've created a generic function Log that will receive the model and a flag (true if the record was created, false if updated)

public function Log($model, $created)
    {
        $message = '';

        if( ! $created) {
            foreach ($model->getDirty() as $attribute => $value) {
                if (substr($attribute, 0, 4) == 'flg_') {
                    $original = (string)(int)$model->getOriginal($attribute);

                    $value = (string)$value == 'true' ? 1 : 0;
                } else
                    $original = $model->getOriginal($attribute);

                if ($original != $value)
                    $message .= $attribute . ' modified from \'' . (substr($attribute, 0, 4) != 'snh_' ? $original : '******') . '\' to \'' . (substr($attribute, 0, 4) != 'snh_' ? $value : '******') . '\'<br>\r\n';
            }
        }
        else
            $message = 'Record created;

        if($message != '')
        {
            $message = '[ID#' . $model->getKey() . '] ' . $message;

            event(new LogEvent($model->getTable(), $message));
        }
    }

public function boot()
    {
        /**
         * User
         */
        User::created(function(User $model) {

            $this->Log($model, true);

        });

        Usuario::updated(function(User $model) {

            $this->Log($model, false);

        });

}

Some considerations: Working with boolean and getDirty() method, I could manage the cast problem.

In my tables, all boolean fields have a "flg_" prefix what is convenient and provide me a way to solve the cast problem. Also, password fields names are preffixed with "snh_" and it's not shown in messages.

Anyway, thanks again and sorry for any mistake in code formatting or in the spelling.

Please or to participate in this conversation.