DEV Community

Mr Banana
Mr Banana

Posted on

Creating a Reusable Trait for Logging User Activities in Laravel Models

In real-world Laravel applications, it's common to need to know who made what changes to which record. In this post, we'll build a reusable Trait that automatically logs basic actions (create, update, delete) for any model that uses it.

Step-by-Step Implementation

  1. Create the activity_logs table
php artisan make:migration create_activity_logs_table
Enter fullscreen mode Exit fullscreen mode

In the migration file:

Schema::create('activity_logs', function (Blueprint $table) {
    $table->id();
    $table->string('model_type');
    $table->unsignedBigInteger('model_id');
    $table->string('action');
    $table->unsignedBigInteger('user_id')->nullable();
    $table->json('changes')->nullable();
    $table->timestamps();
});

Enter fullscreen mode Exit fullscreen mode

Then run:

php artisan migrate
Enter fullscreen mode Exit fullscreen mode
  1. Create the ActivityLog model
php artisan make:model ActivityLog
Enter fullscreen mode Exit fullscreen mode
// app/Models/ActivityLog.php
class ActivityLog extends Model
{
    protected $fillable = [
        'model_type', 'model_id', 'action', 'user_id', 'changes'
    ];

    protected $casts = [
        'changes' => 'array',
    ];
}

Enter fullscreen mode Exit fullscreen mode
  1. Create the reusable Trait
mkdir app/Traits
touch app/Traits/LogsActivity.php
Enter fullscreen mode Exit fullscreen mode
// app/Traits/LogsActivity.php
namespace App\Traits;

use App\Models\ActivityLog;
use Illuminate\Support\Facades\Auth;

trait LogsActivity
{
    public static function bootLogsActivity()
    {
        static::created(function ($model) {
            $model->logActivity('created');
        });

        static::updated(function ($model) {
            $model->logActivity('updated');
        });

        static::deleted(function ($model) {
            $model->logActivity('deleted');
        });
    }

    public function logActivity($action)
    {
        ActivityLog::create([
            'model_type' => get_class($this),
            'model_id' => $this->id,
            'action' => $action,
            'user_id' => Auth::id(),
            'changes' => $this->getChanges(),
        ]);
    }
}

Enter fullscreen mode Exit fullscreen mode
  1. Use it in any model Just import the Trait:
use App\Traits\LogsActivity;

class Post extends Model
{
    use LogsActivity;
    // ...
}

Enter fullscreen mode Exit fullscreen mode

Final Notes
With this simple trait, you can automatically track basic activities on any model. You can easily extend it to support soft deletes, filtering, custom actions, or role-based access.

Top comments (4)

Collapse
 
dimitrim profile image
Dimitri Mostrey

A bit confused here. Using the trait logs any DB changes by simply adding it to the Model? Maybe bootLogsActivity() is a Laravel functionality I never heard of. Or it 'boots' the LogsActivity every time the Model is called. Can you elaborate a little bit how this codes automatically logs DB changes as user activity. Or does it need an additional call?

If it works as is, I salute you. Something to study today.

Collapse
 
mreduar profile image
Eduar Bastidas • Edited

Hi Dimitri,

Yes, simply add the trait to the model and you're all set. There's no need to manually invoke anything or perform any extra "booting."

How does it work so "automagically?"

Convention: boot{TraitName}

When an Eloquent model is loaded, Laravel iterates through all the traits used by the class and searches for a static method named boot{TraitName}.

  • The core handles this within Model::bootTraits(), where it calls bootLogsActivity (or others) during the model's boot cycle.

You can see an example from Laravel itself, such as with SoftDeletes.

You can also read more information and a more detailed explanation here.

Collapse
 
xwero profile image
david duymelinck

Check Spatie Logging activity if you want more well rounded activity logging features.
But the trait is not much different than the one in this post.

Collapse
 
dotallio profile image
Dotallio

Super useful approach! I’ve been looking for a clean way to add activity logs across models without repeating myself - are there any gotchas when using this with soft deletes or mass updates?