danielemontecchi/laravel-userstamps
Installation:
composer require danielemontecchi/laravel-userstamps
Publish the config (optional):
php artisan vendor:publish --provider="DanieleMontecchi\Userstamps\UserstampsServiceProvider"
Configure Database Macros:
Edit config/userstamps.php to define which columns (created_by, updated_by, deleted_by) and their data types (e.g., unsignedBigInteger). Example:
'columns' => [
'created_by' => 'unsignedBigInteger',
'updated_by' => 'unsignedBigInteger',
'deleted_by' => 'unsignedBigInteger',
],
Apply to Models:
Use the HasUserstamps trait in your Eloquent models:
use DanieleMontecchi\Userstamps\HasUserstamps;
class Post extends Model
{
use HasUserstamps;
}
Run Migrations: Publish and run the migration to add the columns to your tables:
php artisan vendor:publish --tag="userstamps-migrations"
php artisan migrate
Audit Trail for a Model:
// Automatically sets `created_by` to the authenticated user
$post = Post::create(['title' => 'Hello World']);
// Manually override (e.g., for admin actions)
$post->setCreatedBy(1); // User ID 1
$post->save();
Automatic Stamping:
created_by, updated_by, and deleted_by when using create(), save(), or delete().Auth facade to fetch the current user (default: auth()->id()).Manual Overrides:
$post->setCreatedBy($userId);
$post->setUpdatedBy($userId);
$post->setDeletedBy($userId);
Query Scopes:
Post::createdBy($userId)->get();
Post::updatedBy($userId)->get();
Post::deletedBy($userId)->get();
Custom User Resolver:
config/userstamps.php:
'user_resolver' => function () {
return auth('api')->id() ?? 0; // Fallback to 0 for unauthenticated
},
APIs: Use middleware to set a default user (e.g., API client ID) for unauthenticated requests:
public function handle($request, Closure $next) {
if (!$request->user()) {
$request->setUserResolver(fn() => $request->header('X-Client-ID'));
}
return $next($request);
}
Commands/Cron Jobs: Pass the user ID explicitly:
$post = new Post();
$post->setCreatedBy($cronUserId)->save();
Soft Deletes:
Ensure deleted_by is set when using delete():
$post->delete(); // Automatically sets `deleted_by`
Observers/Events:
Extend logic in retrieved, saved, or deleted events:
public function saved(Model $model) {
if ($model instanceof Post) {
$model->logActivity("Updated by {$model->updated_by}");
}
}
Migration Conflicts:
Schema::defaultStringLength() if your Laravel version requires it.User Resolver Edge Cases:
auth()->id() returns null, the column will be set to null. Configure a fallback in user_resolver:
'user_resolver' => function () {
return auth()->check() ? auth()->id() : 0; // 0 for "system"
},
Mass Assignment Risks:
$fillable:
protected $fillable = ['title', 'created_by', 'updated_by'];
Soft Deletes + deleted_by:
SoftDeletes, ensure deleted_by is not included in $dates:
protected $dates = ['deleted_at'];
Performance:
created_by, updated_by in queries if unused:
Post::select(['id', 'title'])->get(); // Excludes userstamp columns
Missing Columns: Check if the migration ran and columns exist in the table:
php artisan migrate:status
php artisan schema:dump
User Resolver Not Triggering: Verify the resolver is called by logging it in a model event:
public static function boot() {
static::creating(function ($model) {
\Log::debug('User resolver result:', ['user_id' => app('userstamps')->resolveUser()]);
});
}
Override Not Working:
Ensure setCreatedBy() is called before save():
$post->setCreatedBy(1);
$post->save(); // Now `created_by` is set
Custom Columns:
Extend the trait to support additional columns (e.g., created_ip):
trait HasUserstamps
{
public static function bootHasUserstamps()
{
static::creating(function ($model) {
$model->{$model->getCreatedIpColumn()} = request()->ip();
});
}
public function getCreatedIpColumn()
{
return 'created_ip';
}
}
Database Macros:
Add custom macros to the Userstamps service provider for global query extensions:
// In UserstampsServiceProvider
public function boot()
{
\DB::macro('userstamped', function () {
return $this->whereNotNull('created_by');
});
}
Usage:
Post::query()->userstamped()->get();
Event Hooks: Dispatch custom events when stamps are updated:
// In HasUserstamps trait
public function setUpdatedBy($userId)
{
$this->updated_by = $userId;
event(new UserUpdated($this, $userId));
}
Policy Integration: Use stamps for authorization:
public function update(User $user, Post $post)
{
return $user->id === $post->updated_by;
}
How can I help you explore Laravel packages today?