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 Relationship Events Laravel Package

chelout/laravel-relationship-events

Adds missing Eloquent relationship events to Laravel models. Use simple traits (HasOne/Many, BelongsTo/Many, Morph*) to listen for attach/detach/sync, saved/updated, and other relation lifecycle hooks with parent/related context and IDs/attributes.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require chelout/laravel-relationship-events
    

    Ensure compatibility with your Laravel version (check README).

  2. First Use Case: Add the appropriate trait to your model (e.g., HasOneEvents for one-to-one relationships) and register events in the boot() method:

    use Chelout\RelationshipEvents\Concerns\HasOneEvents;
    
    class User extends Model
    {
        use HasOneEvents;
    
        public static function boot()
        {
            parent::boot();
    
            static::hasOneSaved(function ($parent, $related) {
                Log::info("User's profile saved: {$related->id}");
            });
        }
    
        public function profile()
        {
            return $this->hasOne(Profile::class);
        }
    }
    
  3. Trigger Events: Save a related model to test:

    $user = User::first();
    $user->profile()->save(new Profile(['bio' => 'Test']));
    

    Check logs for the event output.


Implementation Patterns

Core Workflows

  1. Event Registration:

    • Use traits (HasOneEvents, HasManyEvents, etc.) in models.
    • Register events in boot() with closures or dispatchable classes.
    • Example for HasMany:
      static::hasManySaving(function ($parent, $related) {
          // Pre-save logic
      });
      
  2. Observer Integration:

    • Use HasRelationshipObservables trait and define observer methods:
      class UserObserver
      {
          public function hasManyCreating(User $user, Model $related)
          {
              Log::info("Creating related model for user {$user->name}");
          }
      }
      
    • Register observer in AppServiceProvider:
      User::observe(UserObserver::class);
      
  3. Dispatchable Events:

    • Use HasDispatchableEvents trait to fire event classes:
      use Chelout\RelationshipEvents\Traits\HasDispatchableEvents;
      
      class User extends Model
      {
          use HasDispatchableEvents, HasOneEvents;
      
          protected $dispatchesEvents = [
              'hasOneSaved' => HasOneSaved::class,
          ];
      }
      
  4. Polymorphic Relationships:

    • For morphToMany/morphedByMany, listen to events like:
      static::morphToManyAttached(function ($relation, $parent, $ids) {
          Log::info("Attached IDs: " . implode(',', $ids));
      });
      

Integration Tips

  • Conditional Logic: Use events to enforce business rules (e.g., validate related models before saving).
  • Side Effects: Trigger notifications, update caches, or log actions in event handlers.
  • Testing: Mock event handlers in tests to verify behavior:
    $this->expectsEvents(HasOneSaved::class);
    $user->profile()->save(new Profile());
    

Gotchas and Tips

Pitfalls

  1. Dirty Models:

    • Events like belongsToAssociated pass a "dirty" related model (unsaved). Save it explicitly if needed:
      static::belongsToAssociated(function ($relation, $related, $parent) {
          $related->save(); // Ensure model is persisted
      });
      
  2. Additional Queries:

    • Some events (e.g., belongsToDissociated) trigger extra queries to fetch parent models. Optimize with eager loading if performance is critical.
  3. Event Order:

    • Events fire in a specific order (e.g., hasManyCreatinghasManyCreated). Avoid side effects that assume prior events have completed.
  4. Observer vs. Closures:

    • Observers are registered globally; closures are model-specific. Use observers for reusable logic across instances.

Debugging

  • Event Not Firing:

    • Verify the trait is used and boot() is called (e.g., via parent::boot()).
    • Check for typos in event names (e.g., hasOneSaved vs. hasOneSave).
  • Missing Data:

    • Log event payloads to confirm data is passed correctly:
      static::hasManySaved(function ($parent, $related) {
          dd($parent->toArray(), $related->toArray());
      });
      

Extension Points

  1. Custom Events:

    • Extend the package by creating your own event classes and dispatching them in handlers:
      class CustomRelationshipEvent extends Event
      {
          public $parent;
          public $related;
      }
      
      static::hasOneSaved(function ($parent, $related) {
          event(new CustomRelationshipEvent($parent, $related));
      });
      
  2. Dynamic Events:

    • Use dynamic event names for flexible relationships:
      $relationName = 'dynamic_relation';
      static::{$relationName.'Saved'}(function ($parent, $related) {
          // Handle dynamic relation
      });
      
  3. Performance:

    • Batch event handlers for bulk operations to reduce overhead:
      static::hasManySaved(function ($parent, $related) {
          if ($parent->fresh()) {
              // Skip if already processed
              return;
          }
          // Process
      });
      

Configuration Quirks

  • Laravel Version:
    • Ensure compatibility (e.g., v5.x for Laravel 13). Mixing versions may cause issues.
  • Trait Conflicts:
    • Avoid naming conflicts with other traits (e.g., HasEvents). Prefix custom methods if needed.
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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor