Weave 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 Pivot Events Laravel Package

genealabs/laravel-pivot-events

Adds pivot table events to Laravel many-to-many relationships, firing PivotAttached, PivotDetached, PivotUpdated, and PivotSaved so you can hook into attach, detach, and update operations using listeners, observers, or custom pivot models.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Roadmap sequencing for audit and compliance work. Roles, permissions, team memberships, project assignments, tags, and entitlements are almost always belongsToMany relationships — and attach(), detach(), sync(), and updateExistingPivot() bypass normal Eloquent model events entirely. If an audit trail, access review, or SOC 2 control is anywhere on the roadmap, this hook either exists before that work starts or gets retrofitted at the worst possible moment. Adopting it early is a sequencing decision, not a technical one.
  • Feature epics that depend on relationship-change signal. "You were added to Project X" notifications, activity feeds, admin-facing change digests, and customer-facing webhooks are pivot events dressed up as product features. This package turns those epics into listener code instead of domain-layer surgery.
  • Cache invalidation and search-index correctness. A role change should drop the permission cache; a membership change should re-index the user's visible dataset. One central listener replaces scattered, easily-forgotten invalidation calls across controllers. As of 13.1.0, a no-op sync() no longer emits pivotSynced and a detach of rows that were never attached no longer emits pivotDetached — so routine "re-save the same memberships" paths stop generating pointless cache flushes and background jobs. That is a measurable cost reduction on our most common write pattern, and it removes a class of bug where a redundant save masks a stale cache.
  • Seat-based billing and entitlement accuracy. When plan and role memberships live in pivots, seat counts drift silently and only show up in a finance reconciliation. Change-only emission means a resubmitted, unchanged membership form can no longer emit a spurious seat event that billing has to de-duplicate downstream.
  • Data warehouse, CRM, and event-bus propagation. One domain event per genuine pivot change is the cheapest route to getting relationship data into a warehouse or Segment without standing up CDC infrastructure. Idempotent emission lowers de-duplication work for every downstream consumer.
  • Build vs. buy: buy, with a vendoring option. The functional surface is small — one trait plus an event vocabulary — but in-house it is roughly one to two days of engineering plus indefinite maintenance against Laravel's InteractsWithPivotTable internals, which move between majors. MIT license, small readable source, 141 stars, actively released. Vendoring the few dozen lines of the Pivot class is a legitimate hedge if the dependency ever goes quiet.

When to Consider This Package

Adopt when:

  • We have belongsToMany relationships that carry business meaning — roles, permissions, memberships, entitlements — and no reliable way to observe writes to them today. "Just listen to model events" is not an alternative; those writes never fire them.
  • We need one consistent event vocabulary across several models. If it is a single relationship with a single side effect, a direct call at the call site is simpler and cheaper.
  • The writes are human-scale or administrative (role grants, membership edits, admin tooling). That is where the value is concentrated and where the per-row save() cost is invisible.
  • We are already planning audit, notification, or cache-invalidation work within the next couple of releases. Retrofitting the hook mid-epic is where this gets expensive.
  • We can pin a minor version in composer.json and commit to re-running pivot-event tests on every Laravel major upgrade.

Look elsewhere or narrow the scope when:

  • The relationship is a bulk or hot path. Adding the trait forces a custom pivot class, which turns attach()'s single bulk insert() into one save() per row. On a 10k-row sync() that is a real, measurable regression. Keep import pipelines on stock Laravel behaviour and reserve the trait for administrative relations.
  • A single one-off hook would do. One relationship, one side effect, one place in the code — just call it after the write.
  • The relation already uses ->using(MyPivot::class). Laravel's native custom pivot routes writes through real model save()/delete(), so ordinary created/deleted events already fire. Running both mechanisms on one relation is the source of duplicate and missing events. Pick one per relationship and document the choice.
  • A permissions or roles library ships its own pivot class. Third-party pivots frequently shadow the trait's newPivot() override, or get stomped by it. Verify $relation->getPivotClass() before committing.
  • We need intent-level audit ("attempted detach of role 5, no-op"). Since 13.1.0, suppression silently narrows what gets recorded to actual changes. If intent matters, capture it at the call site — do not build that requirement on this event stream.
  • We cannot tolerate the maintenance seam. The package tracks framework internals; 13.1.0 demonstrates that event volume semantics can shift in a minor release.

How to Pitch It (Stakeholders)

To executives: This is a small, MIT-licensed dependency that makes relationship changes — who got which role, who joined which project, who was removed — observable in a single, consistent place. It is the cheapest possible foundation for audit trails, access reviews, customer-facing notifications, and seat-based billing accuracy. Building it ourselves is a day or two of engineering plus an open-ended commitment to track Laravel internals; buying it is a composer line and a pinned version. Since the latest release it also fires only on real changes, which cuts wasted cache flushes and re-index jobs on our most common write pattern.

To engineering: One trait, added to both sides of each belongsToMany relationship we care about (one side only is a silent half-failure — the number one support question in this space). It overrides newPivot() to emit pivotAttaching/pivotAttached/pivotDetaching/pivotDetached/pivotUpdating/pivotUpdated as ordinary string-named Eloquent events, so Event::listen('eloquent.pivotAttached: App\Models\User', ...) and User::pivotAttached(...) both work. The source is a few dozen lines — read Pivot.php before designing on top of it, and dd() the listener argument once, because the subject may be the pivot instance rather than the parent model.

Known costs and constraints we accept: writes become one save() per row instead of a bulk insert, so bulk relations stay on stock behaviour; queue listeners must snapshot pivot attributes before dispatch; listener side effects are only atomic with the write if they share the transaction, and only for synchronous listeners; and the event volume semantics can shift in a minor release, so we pin and re-test on every Laravel upgrade. We should also decide per relationship between this trait and Laravel's native ->using() pivot — not both, which is where duplicate and missing events come from.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource