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

Attribute Events Laravel Package

jpkleemans/attribute-events

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require jpkleemans/attribute-events
    

    No publisher or service provider needed—just add the trait to your models.

  2. First Use Case Define attribute-based events in your model:

    use Jpkleemans\AttributeEvents\AttributeEvents;
    
    class Order extends Model
    {
        use AttributeEvents;
    
        protected $dispatchesEvents = [
            'status:shipped' => OrderShipped::class,
            'note:*' => OrderNoteChanged::class,
        ];
    }
    
    • Key: The syntax attribute:new_value triggers when the attribute changes to new_value.
    • Wildcards (*) allow catching any change to an attribute (e.g., note:*).
  3. Trigger Events

    $order = new Order();
    $order->status = 'shipped'; // Dispatches `OrderShipped` event
    $order->save();
    

Implementation Patterns

Core Workflow

  1. Define Events Use $dispatchesEvents to map attribute changes to event classes:

    protected $dispatchesEvents = [
        'status:pending' => OrderStatusPending::class,
        'status:cancelled' => OrderCancelled::class,
        'priority:high' => HighPriorityOrder::class,
    ];
    
    • Tip: Group related events (e.g., all status changes) for clarity.
  2. Listen to Events Register listeners in EventServiceProvider:

    protected $listen = [
        OrderShipped::class => [
            SendShipmentNotification::class,
        ],
        OrderNoteChanged::class => [
            LogNoteChange::class,
        ],
    ];
    
  3. Access Old/New Values Events receive $model, oldValue, and newValue:

    public function handle(OrderNoteChanged $event)
    {
        Log::info("Note changed from {$event->oldValue} to {$event->newValue}");
    }
    

Advanced Patterns

  • Dynamic Event Mapping Override getDispatchesEvents() to conditionally define events:

    public function getDispatchesEvents()
    {
        if ($this->isPremium()) {
            return array_merge(parent::getDispatchesEvents(), [
                'status:premium' => PremiumOrderStatusChanged::class,
            ]);
        }
        return parent::getDispatchesEvents();
    }
    
  • Mass Assignment Handling Use fill() or update() carefully—events fire after assignment but before saving:

    $order->fill(['status' => 'shipped']); // Event fires before `save()`
    $order->save();
    
  • Event Payload Customization Extend events to include additional data:

    class OrderShipped extends Event
    {
        public function __construct(
            public Order $order,
            public string $oldStatus,
            public string $newStatus,
            public ?User $shippedBy
        ) {}
    }
    
  • Bulk Operations Disable events during bulk updates:

    Order::where('status', 'pending')->update(['status' => 'shipped']);
    // No events fired. Use manual dispatch if needed:
    Order::where('status', 'pending')->get()->each(fn ($order) => $order->dispatchEvents());
    

Gotchas and Tips

Pitfalls

  1. Event Timing

    • Events fire after attribute assignment but before model saving.
    • For pre-save logic, use saving or updating model events instead.
  2. Wildcard Overlap

    • A rule like note:* will catch all changes to note, including those from note:urgent. Order rules by specificity (e.g., note:urgent before note:*).
  3. Case Sensitivity

    • Attribute names and values are case-sensitive:
      // Fails: 'Status:SHIPPED' ≠ 'status:shipped'
      protected $dispatchesEvents = ['Status:SHIPPED' => ...];
      
  4. Circular Dependencies

    • Avoid dispatching events that trigger recursive attribute changes (e.g., status:shipped → updates shipped_at → triggers another event).
  5. Testing Quirks

    • Mock events in tests by overriding shouldDispatchEvent():
      $order->shouldDispatchEvent(OrderShipped::class, false);
      

Debugging Tips

  • Log Events Temporarily add a listener to debug:

    Event::listen(OrderShipped::class, function ($event) {
        Log::debug('Event triggered', ['old' => $event->oldValue, 'new' => $event->newValue]);
    });
    
  • Check Dispatch Order Use dd() in event handlers to inspect the call stack:

    public function handle(OrderShipped $event) {
        dd(debug_backtrace()); // Trace where the event was fired
    }
    

Extension Points

  1. Custom Event Resolvers Override resolveEventClass() to dynamically resolve event classes:

    protected function resolveEventClass(string $eventName): string
    {
        if (str_contains($eventName, 'priority:')) {
            return PriorityChanged::class;
        }
        return parent::resolveEventClass($eventName);
    }
    
  2. Event Payload Modification Extend the base AttributeEvent class to add metadata:

    class CustomAttributeEvent extends AttributeEvent
    {
        public function __construct(
            Model $model,
            string $attribute,
            $oldValue,
            $newValue,
            public array $extraData = []
        ) {
            parent::__construct($model, $attribute, $oldValue, $newValue);
        }
    }
    
  3. Performance Optimization Disable events for non-critical models:

    class Product extends Model
    {
        use AttributeEvents;
    
        protected $dispatchesEvents = []; // No events
    }
    
  4. Queue Events Dispatch events to queues for async processing:

    Event::dispatch(new OrderShipped($order, $oldStatus, $newStatus))
         ->onQueue('orders');
    
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.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
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