Laravel Activity Log: Memonitor Aktivitas User dengan Mudah

Laravel Activity Log: Memonitor Aktivitas User dengan Mudah

id13 min read • 9572 views

Activity Log adalah fitur penting dalam pengembangan aplikasi web yang memungkinkan kita melacak dan merekam aktivitas user dalam sebuah sistem. Dengan mengimplementasikan Activity Log, Kita dapat memantau setiap tindakan yang dilakukan oleh user, seperti membuat, memperbarui, atau menghapus data, serta melacak perubahan yang terjadi pada setiap entitas dalam aplikasi.

Salah satu framework PHP yang populer untuk pengembangan web adalah Laravel. Laravel menyediakan dukungan yang kuat untuk mengimplementasikan Activity Log dengan menggunakan beberapa third party package yang tersedia. Dalam artikel ini, kita akan melihat cara mengimplementasikan Activity Log di Laravel menggunakan salah satu package yang populer yaitu "spatie/laravel-activitylog".

Oke. Langsung saja kita ke topik utama artikel ini. Di artikel ini, saya akan mengajak teman-teman mulai dari menginstall fresh laravel project dan membuat activity log untuk mencatat perubahan dari data Post yang di lakukan oleh user.

Step 1: Install Laravel

Pada step awal, saya akan mengajak kalian untuk menginstall fresh laravel project yang akan kita gunakan untuk kebutuhan tutorial ini. Jika kalian sudah punya laravel project, kalian bisa mengabaikan step ini. Disini saya akan menginstall laravel project dengan nama laravel-activity-log.

laravel new laravel-activity-log

Step 2: Install Laravel UI

Tentunya kita juga memerlukan fitur authentication yang nantinya setiap dari aktivitas user tercatat sebagai log. Nah, untuk membuat fitur authentication di laravel, disini saya akan memakai laravel ui package. Untuk installasi laravel ui package, bisa menggunakan perintah-perintah seperti di bawah ini secara berurutan.

composer require laravel/ui
php artisan ui bootstrap --auth
npm install && npm run dev

Step 3: Install Laravel Activity Log

Oke. Pada step ketiga ini kita akan menginstall activity log package menggunakan perintah seperti di bawah ini.

composer require spatie/laravel-activitylog

Pubish file migration dari package tersebut dengan menjalankan perintah artisan seperti di bawah ini.

php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-migrations"

Jalankan perintah seperti di bawah ini untuk mempublish file config. Optional.

php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-config"

Step 4: Setup Database, Model & Migration

Kita membuat database baru untuk menampung data yang akan akan digunakan. Jika sudah selesai membuat database baru, jangan lupa untuk menyesuaikan value pada key DB_DATABASE di file .ENV seperti pada contoh di bawah ini.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_activity_log
DB_USERNAME=root
DB_PASSWORD=

Pada artikel ini, kita akan membuat activity log untuk setiap perubahan data pada tabel posts. Untuk itu, mari kita generate file model dan migration untuk tabel posts dengan menjalankan perintah artisan seperti di bawah ini.

php artisan make:model Post -m

Setelah perintah di atas dijalankan, sekarang buka file migration posts yang berhasil dibuat dan ubah method up menjadi seperti di bawah ini.

public function up(): void
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id');
        $table->string('title');
        $table->string('slug')->unique();
        $table->longText('content');
        $table->timestamps();
    });
}

Pada tabel posts, kita akan menambahkan foreign key yang berelasi dengan tabel users.

Selanjutnya, buka model Post dan ubah kodenya seperti di bawah ini.

Untuk merekam aktivitas, Kita perlu menggunakan fitur LogsActivity yang disediakan oleh laravel activity log package ini. Kita dapat menerapkannya pada model "Post" dengan menambahkan use LogsActivity dan LogsActivity trait di model tersebut.

<?php

namespace App\Models;
use Spatie\Activitylog\LogOptions;
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\Traits\LogsActivity;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Post extends Model
{
    use HasFactory, LogsActivity;
    
    protected $guarded = [];

    public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults()
                ->logOnly(['title', 'slug','content'])
                ->setDescriptionForEvent(fn(string $eventName) => "This model has been {$eventName}")
                ->useLogName('Post');
    }

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

Dalam contoh di atas, kita menambahkan function getActivityLogOptions() untuk mendefinisikan log yang akan tercatat dari aktivitas yang dilakukan terhadap model Post tersebut. Pada contoh tersebut, saya hanya akan mencatat log untuk perubahan pada attribute title, slug dan content. Selain itu, kita juga mengatur deskripsinya dan log name yang akan tercatat. Tidak ketinggalan, di model Post kita juga menambahkan relasi antara model Post dengan User.

Oke, selanjutnya kita pindah lagi ke model User untuk menambahkan relasi antara model User dengan model Post.

public function posts(){
    return $this->hasMany(Post::class);
}

Step 5: Setup Controller

Berikutnya, mari kita buat controller baru untuk menambahkan logic dari CRUD Post. Jalankan perintah artisan seperti di bawah ini untuk membuat file PostController.php.

php artisan make:controller PostController

Buka file PostController yang baru saja dibuat dan ubah kodenya menjadi seperti di bawah ini.

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;
use Spatie\Activitylog\Models\Activity;

class PostController extends Controller
{
    public function index(){
        return view('posts.index',[
            'posts' => Post::with(['user'])->latest()->paginate(20)
        ]);
    }

    public function create(){
        return view('posts.form',[
            'method' => 'POST',
            'post'   => new Post(),
            'route'  => route('posts.store'),
        ]);
    }

    public function store(Request $request){
        auth()->user()->posts()->create([
            'title'   => $request->title,
            'slug'    => \Str::slug($request->title),
            'content' => $request->content
        ]);

        return to_route('posts.index')->with('success','Post created!');
    }

    public function edit(Post $post){
        return view('posts.form',[
            'method' => 'PUT',
            'post'   => $post,
            'route'  => route('posts.update',$post),
        ]);
    }

    public function update(Request $request, Post $post){
        $post->update([
            'title'   => $request->title,
            'slug'    => \Str::slug($request->title),
            'content' => $request->content
        ]);

        return to_route('posts.index')->with('success','Post updated!');
    }

    public function log(Post $post){
        return view('posts.log',[
            'logs' => Activity::where('subject_type',Post::class)->where('subject_id',$post->id)->latest()->get()
        ]);
    }

}

Contoh di atas hanya berisikan function biasa untuk CRUD Post. Perhatikan pada method log. Method tersebut berfungsi untuk mengambil activity log dari object Post yang akan kita tampilkan pada view log.

Step 6: Setup View

Oke, kita lanjut ke view. Mari kita buat file view baru dengan nama index.blade.php. Tambahkan kode di bawah ini pada file view tersebut.

resources/views/posts/index.blade.php

@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">
                    <a href="{{ route('posts.create') }}" class="btn btn-primary">
                    + New Post
                    </a>
                </div>
                <div class="card-body">
                    <table class="table table-striped">
                        <thead>
                            <tr>
                                <th scope="col" width="5%">#</th>
                                <th scope="col" width="55%">Title</th>
                                <th scope="col" width="30%">User</th>
                                <th scope="col" width="10%">Action</th>
                            </tr>
                        </thead>
                        <tbody>
                            @forelse ($posts as $key => $post)
                            <tr>
                                <th scope="row">{{ ++$key }}</th>
                                <td>{{ $post->title }}</td>
                                <td>{{ $post->user->name }}</td>
                                <td>
                                    <div class="dropdown">
                                        <button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
                                        Action
                                        </button>
                                        <ul class="dropdown-menu">
                                            <li><a class="dropdown-item" href="{{ route('posts.edit', $post->id) }}">Edit</a></li>
                                            <li><a class="dropdown-item" href="{{ route('posts.log',$post->id) }}">Log</a></li>
                                        </ul>
                                    </div>
                                </td>
                            </tr>
                            @empty
                            <tr>
                                <td colspan="4" class="text-center">No posts found.</td>
                            </tr>
                            @endforelse
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Contoh di atas hanya view biasa yang berisikan tabel untuk menampilkan data dari tabel posts dan menambahkan action edit dan log pada setiap barisnya.

Kemudian, buat file view baru lagi di dalam direktori views/posts dengan nama form.blade.php. Tambahkan kode seperti di bawah ini ke file view tersebut.

resources/views/posts/form.blade.php

@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-body">
                    <form action="{{ $route }}" method="post">
                        @if ($method == 'PUT') @method('PUT') @endif
                        @csrf
                        <div class="mb-3">
                            <label for="title" class="form-label">Title</label>
                            <input type="text" name="title" id="title" class="form-control" value="{{ old('title',$post->title) }}">
                        </div>
                        <div class="mb-3">
                            <label for="content" class="form-label">Content</label>
                            <textarea name="content" id="content" class="form-control">{{ old('content',$post->content) }}</textarea>
                        </div>
                        <button type="submit" class="btn btn-primary">
                        Save
                        </button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Kode di atas merupakan view form biasa yang akan digunakan oleh method create dan edit.

File view terakhir yang perlu kita tambahkan adalah log.blade.php, yang akan kita fungsikan untuk menampilkan activity log dari object post. Silakan buat file view tersebut dan tambahkan kode seperti di bawah ini.

resources/views/posts/log.blade.php

@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">
                    Activity Log
                </div>
                <div class="card-body">
                    <div class="accordion" id="accordionExample">
                        @forelse ($logs as $key => $log)
                        <div class="accordion-item">
                            <h2 class="accordion-header">
                                <button class="accordion-button {{ $key != 0 ? 'collapsed' : '' }}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-{{ $key }}" aria-expanded="true" aria-controls="collapse-{{ $key }}">
                                <span>{{ $log->description }}</span> <small class="text-muted ms-2 pb-1">({{ $log->created_at->diffForHumans() }})</small>
                                </button>
                            </h2>
                            <div id="collapse-{{ $key }}" class="accordion-collapse collapse {{ $key == 0 ? 'show' : '' }}" data-bs-parent="#accordionExample">
                                <div class="accordion-body">
                                    @if ($log->event == 'updated')
                                    <ul class="list-group">
                                        <li class="list-group-item bg-secondary text-white">Old Data</li>
                                        <li class="list-group-item"><strong>Title:</strong> {{ $log['properties']['old']['title'] }}</li>
                                        <li class="list-group-item"><strong>Slug:</strong> {{ $log['properties']['old']['slug'] }}</li>
                                        <li class="list-group-item"><strong>Content:</strong> {{ $log['properties']['old']['content'] }}</li>
                                        <li class="list-group-item bg-secondary text-white">New Data</li>
                                        <li class="list-group-item"><strong>Title:</strong> {{ $log['properties']['attributes']['title'] }}</li>
                                        <li class="list-group-item"><strong>Slug:</strong> {{ $log['properties']['attributes']['slug'] }}</li>
                                        <li class="list-group-item"><strong>Content:</strong> {{ $log['properties']['attributes']['content'] }}</li>
                                    </ul>
                                    @else
                                    {{ $log->description }}
                                    @endif
                                </div>
                            </div>
                        </div>
                        @empty
                        <div class="accordion-item">
                            <h2 class="accordion-header">
                                <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-{{ $key }}" aria-expanded="true" aria-controls="collapse-{{ $key }}">
                                No activity found.
                                </button>
                            </h2>
                        </div>
                        @endforelse
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Pada view log, kita akan menampilkan detail perubahan (data lama & data baru) untuk event updated, dan selain event updated kita akan menampilkan data description dari activity log.

Step 7: Define Route

Jika model, view dan controller sudah kita buat dan kita siapkan, berikutnya kita perlu mendaftarkan route yang akan kita arahkan ke method-method yang telah kita define di PostController. Buka file routes/web.php, lalu tambahkan kode di bawah ini ke file route tersebut.

Route::middleware(['auth'])->group(function(){
    Route::get('posts/{post}/log', [App\Http\Controllers\PostController::class, 'log'])->name('posts.log');
    Route::resource('posts', App\Http\Controllers\PostController::class);
});

Step 8: Testing

Oke, setelah mengikuti langkah-langkah di atas, sekarang waktunya kita untuk menguji apakah activity log sudah terimplementasi dengan baik atau belum. Silakan jalankan laravel project kalian dan buka project tersebut pada browser untuk melakukan register atau login.

Jika sudah login, coba buka halaman posts dan coba lakukan tambah atau edit data post. Setiap ada aktivitas baik itu tambah, edit atau hapus data maka activity log akan tercatat dan ditambahkan ke tabel activity_logs seperti gambar di bawah ini.

Gambar di bawah ini merupakan contoh activity log yang tercatat dari setiap aktivitas model Post.

laravel activity log

Kemudian jika sudah mencoba tambah atau edit data, coba buka halaman log dari salah satu data post, maka akan menampilkan halaman seperti di bawah ini.

laravel activity log

Pada halaman log, kita menggunakan component accordian dari bootstrap untuk menampilkan activity log dari data post tersebut.

Kesimpulan

Mengimplementasikan Activity Log di Laravel dengan menggunakan package "spatie/laravel-activitylog" adalah langkah yang penting untuk memantau aktivitas user dalam aplikasi. Dengan merekam aktivitas pengguna, Kita dapat memelihara catatan yang berguna untuk melacak perubahan yang terjadi pada entitas dan mengidentifikasi tindakan yang dilakukan oleh user.

Dalam artikel ini, kami telah menjelaskan langkah-langkah dasar untuk mengimplementasikan Activity Log di Laravel menggunakan package "spatie/laravel-activitylog". Dengan mengikuti langkah-langkah ini, temen-temen dapat mengintegrasikan fitur Activity Log ke dalam aplikasi Laravel kalian dan meningkatkan pengalaman user serta keamanan sistem kalian.

Source code: Laravel Activity Log

Full Documentation: Laravel Activity Log

Published on July 16, 2023
Last updated on April 21, 2025

If you like this post and want to support us, you can support us via buymeacoffee or trakteer.