richan-fongdasen/eloquent-blameable
Installation:
composer require richan-fongdasen/eloquent-blameable
Add the service provider to config/app.php under providers:
RichanFongdasen\EloquentBlameable\BlameableServiceProvider::class,
Enable Traits:
Use the Blameable trait in your Eloquent model:
use RichanFongdasen\EloquentBlameable\Blameable;
class Post extends Model
{
use Blameable;
}
Run Migrations:
Publish and run the migration to add created_by and updated_by columns:
php artisan vendor:publish --provider="RichanFongdasen\EloquentBlameable\BlameableServiceProvider" --tag=migrations
php artisan migrate
First Use Case: Automatically track who creates/updates a record:
$post = new Post(['title' => 'Hello World']);
$post->save(); // `created_by` and `updated_by` set to authenticated user ID
Automatic Tracking:
created_by and updated_by with the authenticated user’s ID (via Auth::id()).$post->setCreatedBy($userId)->save();
$post->setUpdatedBy($userId)->save();
Manual Overrides:
$post->update(['title' => 'Updated'], ['updated_by' => $adminId]);
Query Scoping:
$postsByUser = Post::whereCreatedBy(Auth::id())->get();
$recentlyUpdated = Post::whereUpdatedBy(Auth::id())->latest()->take(10)->get();
API Integration:
$request->validate(['updated_by' => 'sometimes|integer']);
$post->update($request->except('updated_by'), ['updated_by' => $request->updated_by]);
Soft Deletes:
deleted_by by publishing the config:
php artisan vendor:publish --provider="RichanFongdasen\EloquentBlameable\BlameableServiceProvider" --tag=config
Then set blameable_deleted_by to true in config/blameable.php.Middleware: Attach blameable logic to middleware for bulk operations:
public function handle($request, Closure $next)
{
if ($request->is('admin/*')) {
$request->merge(['blameable_user' => Auth::id()]);
}
return $next($request);
}
Events:
Listen for creating, updating, or saved events to customize blameable behavior:
Post::created(function ($post) {
if ($post->wasRecentlyCreated && $post->created_by === null) {
$post->setCreatedBy(1); // Fallback to default user
}
});
Testing: Mock the blameable user in tests:
$this->actingAs($user);
$post = Post::create(['title' => 'Test']);
$post->fresh()->created_by; // Asserts $user->id
Authentication Dependence:
created_by/updated_by will be null if no user is authenticated. Handle this in your model’s boot():
protected static function bootBlameable()
{
static::creating(function ($model) {
if (auth()->check()) {
$model->setCreatedBy(auth()->id());
}
});
}
Mass Assignment Risks:
$fillable or use $guarded:
protected $fillable = ['title', 'content'];
// OR
protected $guarded = ['created_by', 'updated_by'];
Soft Deletes Conflict:
SoftDeletes, ensure deleted_by is added to the model’s $dates array:
use Illuminate\Database\Eloquent\SoftDeletes;
use RichanFongdasen\EloquentBlameable\Blameable;
class Post extends Model
{
use SoftDeletes, Blameable;
protected $dates = ['deleted_at', 'created_by', 'updated_by', 'deleted_by'];
}
Performance:
with() sparingly:
// Bad: Loads all users for every post
Post::with('creator')->get();
// Good: Load only when needed
$post->creator; // Lazy-loaded
Missing Columns:
schema::hasColumn() in a Tinker session:
php artisan tinker
>>> \Schema::hasColumn('posts', 'created_by')
Null Values:
// Override the resolver in config/blameable.php
'resolver' => function () {
return auth()->check() ? auth()->id() : null;
},
Custom Resolvers:
config/blameable.php:
'resolver' => function () {
return request()->header('X-Blameable-ID') ?? auth()->id();
},
Dynamic Columns:
published_by):
use RichanFongdasen\EloquentBlameable\Concerns\Blameable as BaseBlameable;
trait CustomBlameable
{
use BaseBlameable;
protected $blameable = ['created_by', 'updated_by', 'published_by'];
}
Audit Logging:
spatie/laravel-activitylog to log blameable actions:
use Spatie\Activitylog\LogOptions;
$post->logActivity('updated', new LogOptions(['only' => ['title'], 'extra' => ['blameable' => $post->updated_by]]));
Polymorphic Blameable:
blameable_type/blameable_id pattern:
// In config/blameable.php
'morph_map' => [
'user' => [User::class, 'id'],
'admin' => [Admin::class, 'id'],
],
How can I help you explore Laravel packages today?