Laravel 12 Model Changelog Feature Integration

Using laravel 12, we can easily create a model change log. Laravel model change log is a feature where every changes of model attributes are tracked and saved in system. It can be saved in database or file. Laravel observer does this awesome task. Here in this article we will see how we can create a simple model change log feature in our laravel application. Laravel spatie permission package provides this type of feature. We have just created our own simplified way for getting model changes. Let's start

  1. Install laravel 12
  2. Make observer class
  3. Make ModelLog Class and Migration
  4. Update AppServiceProvider
  5. Run the application
Install Laravel 12 Application

To reach our goal we will initially create an empty laravel 12 application. You can also do this in your existing laravel 12 application as well

composer create-project --prefer-dist laravel/laravel laravel12-changelog
cd changelog

Make Observer Class

After successful creation of laravel 12, it's time to create Observer class. This observer class will be the key role for tracking model changes activity such as create, update as well as delete

php artisan make:observer ModelChangeObserver
namespace App\Observers;

use App\Models\ModelLog;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;

class ModelChangeObserver
{
    public function created(Model $model)
    {
        $this->logChange('created', $model);
    }

    public function updated(Model $model)
    {
        $this->logChange('updated', $model);
    }

    public function deleted(Model $model)
    {
        $this->logChange('deleted', $model);
    }

    protected function logChange($event, Model $model)
    {
        $changeLogDriver = config('ecom.model_log');

        if (!in_array($changeLogDriver, ['file', 'database'])) {
            throw new \Exception("Change log driver `$changeLogDriver` not supported");
        }

        switch ($changeLogDriver) {
            case 'file':
                Log::channel('model_changes')->info("Model " . get_class($model) . " was {$event}", [
                    'model_id' => $model->getKey(),
                    'attributes' => $model->getAttributes(),
                    'changed' => $model->getChanges(),
                    'user_id' => auth()->id(),
                ]);
                break;

            case 'database':
                ModelLog::create([
                    'model_type' => get_class($model),
                    'model_id' => $model->getKey(),
                    'event' => $event,
                    'attributes' => $model->getAttributes(),
                    'changes' => $model->getChanges(),
                    'user_id' => auth()->id(),
                ]);
                break;
        }

    }
}

Along with this we will create a file at config/ecom.php . This will determine whether log will be saved in database or file.

return [ //config/ecom.php
    'model_log' => 'database', //database,file
];

Make ModelLog Model and Necessary Migration file

Here we will create a ModelLog model along with migration so that every changes of our selected model is saved in file or data. 

php artisan make:model ModelLog -m
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class ModelLog extends Model
{ //model
    protected $fillable = [
        'model_type',
        'model_id',
        'event',
        'attributes',
        'changes',
        'user_id',
    ];

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

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Migration

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('model_logs', function (Blueprint $table) {
            $table->id();
            $table->string('model_type');
            $table->unsignedBigInteger('model_id')->nullable();
            $table->string('event');
            $table->json('attributes')->nullable();
            $table->json('changes')->nullable();
            $table->unsignedBigInteger('user_id')->nullable();
            $table->timestamps();
        });

    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('model_logs');
    }
};

Update AppServiceProvider and Register Model for Observe

Here in this stage, we will add models those will be tracked for logging.

namespace App\Providers;

use App\AppSetting;
use App\Observers\ModelChangeObserver;
use Illuminate\Support\ServiceProvider;

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

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        \App\Models\User::observe(ModelChangeObserver::class);
        \App\Models\Product::observe(ModelChangeObserver::class);
        \App\Models\Category::observe(ModelChangeObserver::class);
        \App\Models\Brand::observe(ModelChangeObserver::class);
        \App\Models\AttributeValue::observe(ModelChangeObserver::class);
        \App\Models\PaymentGateway::observe(ModelChangeObserver::class);
        \App\Models\CourierGateway::observe(ModelChangeObserver::class);
        \App\Models\SmsGateway::observe(ModelChangeObserver::class);
    }
}

Let's run the app and do some actions in different pages for store, update or delete. This will store logs according to our selected model_change value. 


Tags: