Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Eloquent Blameable Laravel Package

richan-fongdasen/eloquent-blameable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require richan-fongdasen/eloquent-blameable
    

    Add the service provider to config/app.php under providers:

    RichanFongdasen\EloquentBlameable\BlameableServiceProvider::class,
    
  2. Enable Traits: Use the Blameable trait in your Eloquent model:

    use RichanFongdasen\EloquentBlameable\Blameable;
    
    class Post extends Model
    {
        use Blameable;
    }
    
  3. 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
    
  4. 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
    

Implementation Patterns

Core Workflows

  1. Automatic Tracking:

    • The trait auto-fills created_by and updated_by with the authenticated user’s ID (via Auth::id()).
    • Override defaults per operation:
      $post->setCreatedBy($userId)->save();
      $post->setUpdatedBy($userId)->save();
      
  2. Manual Overrides:

    • Useful for admin actions or API calls where the "blamer" isn’t the authenticated user:
      $post->update(['title' => 'Updated'], ['updated_by' => $adminId]);
      
  3. Query Scoping:

    • Filter records by creator/updater:
      $postsByUser = Post::whereCreatedBy(Auth::id())->get();
      $recentlyUpdated = Post::whereUpdatedBy(Auth::id())->latest()->take(10)->get();
      
  4. API Integration:

    • Pass blameable IDs via request headers or payload:
      $request->validate(['updated_by' => 'sometimes|integer']);
      $post->update($request->except('updated_by'), ['updated_by' => $request->updated_by]);
      
  5. Soft Deletes:

    • Extend blameable tracking to 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.

Integration Tips

  • 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
    

Gotchas and Tips

Pitfalls

  1. 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());
              }
          });
      }
      
  2. Mass Assignment Risks:

    • Explicitly whitelist blameable fields in $fillable or use $guarded:
      protected $fillable = ['title', 'content'];
      // OR
      protected $guarded = ['created_by', 'updated_by'];
      
  3. Soft Deletes Conflict:

    • If using 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'];
      }
      
  4. Performance:

    • Avoid eager-loading blameable relationships unless necessary. Use with() sparingly:
      // Bad: Loads all users for every post
      Post::with('creator')->get();
      
      // Good: Load only when needed
      $post->creator; // Lazy-loaded
      

Debugging

  • Missing Columns:

    • Verify the migration was run and columns exist in the database. Check schema::hasColumn() in a Tinker session:
      php artisan tinker
      >>> \Schema::hasColumn('posts', 'created_by')
      
  • Null Values:

    • Debug the blameable resolver chain:
      // Override the resolver in config/blameable.php
      'resolver' => function () {
          return auth()->check() ? auth()->id() : null;
      },
      

Extension Points

  1. Custom Resolvers:

    • Override the blameable user resolver in config/blameable.php:
      'resolver' => function () {
          return request()->header('X-Blameable-ID') ?? auth()->id();
      },
      
  2. Dynamic Columns:

    • Extend the trait to support custom blameable columns (e.g., published_by):
      use RichanFongdasen\EloquentBlameable\Concerns\Blameable as BaseBlameable;
      
      trait CustomBlameable
      {
          use BaseBlameable;
      
          protected $blameable = ['created_by', 'updated_by', 'published_by'];
      }
      
  3. Audit Logging:

    • Combine with spatie/laravel-activitylog to log blameable actions:
      use Spatie\Activitylog\LogOptions;
      
      $post->logActivity('updated', new LogOptions(['only' => ['title'], 'extra' => ['blameable' => $post->updated_by]]));
      
  4. Polymorphic Blameable:

    • For polymorphic relationships, use a blameable_type/blameable_id pattern:
      // In config/blameable.php
      'morph_map' => [
          'user' => [User::class, 'id'],
          'admin' => [Admin::class, 'id'],
      ],
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware