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

Laravel Transactional Model Events Laravel Package

mvanduijker/laravel-transactional-model-events

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require mvanduijker/laravel-transactional-model-events
    

    No publisher or config required—just add the trait.

  2. First Use Case: Add TransactionalAwareEvents to a model (or base model):

    use Mvanduijker\LaravelTransactionalModelEvents\TransactionalAwareEvents;
    
    class User extends Model
    {
        use TransactionalAwareEvents;
    }
    
  3. Listen for Events: Register listeners in EventServiceProvider:

    protected $listen = [
        'afterCommit.created:App\Models\User' => [
            'App\Listeners\HandleUserCreation',
        ],
        'afterRollback.saved:App\Models\User' => [
            'App\Listeners\HandleUserUpdateRollback',
        ],
    ];
    
  4. Trigger Events: Use DB::transaction() as usual—events fire after commit/rollback:

    DB::transaction(function () {
        $user = User::create([...]); // Triggers `afterCommit.created`
        $user->update([...]);        // Triggers `afterCommit.saved`
    });
    

Implementation Patterns

Core Workflows

  1. Event Granularity:

    • afterCommit.created: Fired when a model is created in a transaction.
    • afterCommit.saved: Fired when a model is updated in a transaction.
    • afterRollback.created/afterRollback.saved: Fired if the transaction rolls back.
    • afterCommit.deleted: For soft/deletes (if using SoftDeletes trait).
  2. Base Model Integration: Extend a base model to apply transactional events globally:

    class BaseModel extends Model
    {
        use TransactionalAwareEvents;
    }
    

    All child models inherit event support.

  3. Dynamic Event Handling: Use wildcards in listeners for reusable logic:

    'afterCommit.*.App\Models\User' => [UserEventHandler::class],
    
  4. Transaction Scoping:

    • Events fire only for models modified within the same transaction.
    • Nested transactions (e.g., DB::transaction(fn => DB::transaction(...))) work as expected.
  5. Queue Integration: Dispatch queued jobs in listeners (events fire post-transaction):

    public function handle(AfterCommitCreated $event) {
        SendWelcomeEmail::dispatch($event->model);
    }
    

Integration Tips

  • Testing: Use DB::fake() + DB::transaction() to test rollback scenarios:

    DB::transaction(function () {
        User::create([...]);
        $this->assertEventFired(AfterRollbackCreated::class);
        throw new \Exception('Simulate rollback');
    });
    
  • Performance: Avoid heavy logic in listeners—events fire after the transaction completes.

  • Model Events Conflict: Regular Eloquent events (creating, saved) still fire before transactional events. Use transactional events for post-commit logic (e.g., notifications, analytics).


Gotchas and Tips

Pitfalls

  1. Event Timing:

    • Not immediate: Events fire after the transaction completes (commit/rollback). ❌ Avoid assuming events fire synchronously within the same transaction block.
    • Rollback behavior: If the transaction rolls back, only afterRollback.* events fire—no afterCommit.*.
  2. Model State:

    • Detached models: Events won’t fire for models detached from the transaction (e.g., loaded outside the transaction).
    • Lazy loading: Ensure all model interactions (e.g., update(), delete()) happen within the transaction.
  3. Soft Deletes:

    • Requires SoftDeletes trait for afterCommit.deleted to work.
    • Hard deletes (forceDelete()) do not trigger this event.
  4. Database Connections:

    • Events only work with the default connection (configurable in config/database.php).
    • Multi-connection setups may require manual binding.
  5. Event Payload:

    • The $event->model is a detached copy (not managed by the transaction). ⚠️ Avoid modifying it—use fresh() if you need the latest state.

Debugging

  • Verify Events: Check if events fire with:

    Event::listen('afterCommit.*', function ($event) {
        logger()->debug('Transactional event fired:', ['model' => $event->model]);
    });
    
  • Transaction Boundaries: Ensure all model operations are within DB::transaction(). Events won’t fire for:

    // ❌ No event (outside transaction)
    User::create([...]);
    
    DB::transaction(function () {
        // ✅ Events fire here
        User::create([...]);
    });
    
  • Rollback Testing: Simulate rollbacks with DB::rollBack() in tests or by throwing exceptions.

Extension Points

  1. Custom Events: Extend the trait to add domain-specific events:

    use Mvanduijker\LaravelTransactionalModelEvents\TransactionalAwareEvents;
    
    class User extends Model
    {
        use TransactionalAwareEvents;
    
        protected static function bootTransactionalEvents()
        {
            static::afterCommit(function ($model) {
                if ($model->isAdmin()) {
                    event(new AdminCreated($model));
                }
            });
        }
    }
    
  2. Conditional Listeners: Dynamically register listeners based on model attributes:

    if ($user->isActive()) {
        event(new UserActivated($user));
    }
    
  3. Global Configuration: Override default behavior via service provider:

    public function boot()
    {
        TransactionalAwareEvents::setEventPrefix('custom.');
    }
    
  4. Queue Failures: Handle listener failures gracefully (e.g., retry with ShouldQueue):

    public function handle(AfterCommitCreated $event)
    {
        try {
            SendEmail::dispatch($event->model);
        } catch (\Throwable $e) {
            report($e);
        }
    }
    
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope