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

chelout/laravel-relationship-events

Adds the missing relationship events to Laravel Eloquent models. Traits for hasOne, belongsTo, hasMany, belongsToMany, morphOne, morphTo, morphMany, morphToMany, and morphedByMany let you listen for saved, updated, attached, detached, and other events, useful for cache invalidation and audit logs.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package fills a genuine gap in Eloquent: Laravel fires model lifecycle events (creating, saved, deleted) but has no first-class hooks for relationship operations such as attach, detach, sync, associate, dissociate, or save on a relation. In our architecture, that gap is usually filled either by explicit domain events in service/action classes or by ad-hoc observer logic scattered across models. This package offers a third option — declarative, per-relation-type event hooks registered on the model itself — that fits cleanly into a standard Laravel domain layer without introducing a service container binding, config file, or schema change. For teams already using model observers (documented pattern with HasRelationshipObservables), it extends an existing convention rather than introducing a new one. Best fit: small-to-mid Laravel apps with pivot-heavy domains (RBAC, tagging, memberships) where auditing and side effects (cache invalidation, indexing, notifications) need to fire on relationship mutation.

Integration feasibility. Low-effort, additive integration. composer require plus a trait per model per relation type (HasOneEvents, HasBelongsToManyEvents, HasMorphManyEvents, and six more), with listeners registered in boot() or via observer classes. Optional HasDispatchableEvents lets us route to real event classes via $dispatchesEvents, which is the path a TPM should standardize on — it keeps listeners queueable, testable, and decoupled from the model. No migrations, no config publishing, no service provider registration required. The compatibility matrix is explicit and tracks Laravel majors (v5.x→Laravel 13/PHP 8.3, v4.x→12, v3.x→11, v2.x→10), which means version pinning maps 1:1 to our framework upgrade train.

Technical risk. The core risk is that the package achieves this by swapping in its own relationship classes, which means it sits on top of Eloquent internals rather than a public extension point. That makes it sensitive to framework-level refactors in HasOneOrMany, BelongsToMany, and MorphTo. The package has kept pace through five Laravel majors, which is reassuring, but it is a single-maintainer project (526 stars, 0 dependents on Packagist) — no downstream packages exercise it, so we would be among the more demanding consumers. Secondary risks: the README still lists "Tests, tests, tests" as a TODO, so coverage depth is unverified and we should treat correctness of edge paths (custom pivot models, syncWithoutDetaching, toggle, updateExistingPivot, chunked/collection attaches) as unproven until we test them. Documentation has visible defects (the belongsTo example in the many-to-many doc labels hasManySaving/hasManySaved for a belongsTo save; the morphTo doc is truncated), which signals docs are not carefully curated — budget for empirical verification. There is also documented extra query cost: detach/sync/toggle issue an additional query to resolve affected IDs, and HasMany::update loads a collection, both of which can become N+1 or memory hotspots in bulk operations if listeners are naive.

Key questions.

  1. What is our actual requirement — audit trail/compliance, side-effect orchestration, or both? If audit, do we need guaranteed capture on every mutation path (raw query builder, DB::table, queued jobs, imports), which no Eloquent-layer package can give?
  2. Does our codebase override or extend Eloquent relationship classes anywhere (custom Relation subclasses, relation macros, packages that patch BelongsToMany)? Any collision here is a hard blocker.
  3. What are the exact mutation APIs used in our code today (attach, sync, syncWithoutDetaching, toggle, updateExistingPivot, pivot-model events)? This defines the test matrix.
  4. Are listeners expected to run inside the writing transaction or after commit? Does the package's firing point sit pre- or post-commit for our transactional service layer?
  5. Do we accept single-maintainer bus factor, or do we need a documented exit plan (vendor fork, or migration to explicit domain events)?

Integration Approach

Stack fit. Native fit for a conventional Laravel monolith or modular monolith on Laravel 10–13. Trait-based, opt-in per model, no framework-level patching, no global middleware, no runtime bootstrapping beyond the trait. Compatible with standard observer registration in AppServiceProvider::boot() and with Laravel's event dispatcher, so listeners can implement ShouldQueue and use the normal queue/retry infrastructure. It does not require or conflict with Redis, Horizon, Octane semantics beyond normal model boot, or our existing observability stack.

Migration path. Entirely additive and reversible on a per-model basis: add trait → register listeners → ship. No schema change, no data backfill, no dual-write period. Removal is equally mechanical (drop trait and listeners), making this a low-commitment decision we can pilot and roll back. Note that behavior added before any listener exists is a no-op, so incremental adoption carries no risk of silent behavior change.

Compatibility. Pin the package minor version to our Laravel major (v4.x on Laravel 12, v5.x on Laravel 13) and add it to the framework-upgrade checklist alongside our own code. PHP floor rises to 8.2/8.3 depending on branch — confirm against our deployment images before adopting v5.x. Verify coexistence with any package that decorates Eloquent relations (activity logging, auditing, tenancy packages that scope relations) during the pilot, not after.

Sequencing.

  1. Spike (1–2 days): install on a branch, apply one trait to one pivot-heavy model (e.g., User role assignments), and empirically validate attach/detach/sync/toggle/updateExistingPivot event payloads and ordering against our real relation definitions, including a custom Pivot model.
  2. Pattern decision: standardize on HasDispatchableEvents + dedicated event classes, not closures in boot(). Closures are convenient but untestable in isolation and invisible to static analysis; a TPM should reject them as the house style before they spread.
  3. Pilot domain: instrument one bounded feature (audit log or cache invalidation for memberships), with listeners queued afterCommit and a feature-flagged rollout.
  4. Hardening: write our own integration tests around the specific mutations we use, since upstream test depth is unverified; measure query counts before/after on the hottest bulk paths (sync of large ID sets, HasMany::update).
  5. Broaden: extend to remaining relation types per model, one PR per model, with listener coverage asserted in tests.

Operational Impact

Maintenance. Low ongoing operational burden: no daemon, no storage, no config drift. The real maintenance cost is upgrade-coupled — each Laravel major requires a coordinated package bump, and we should assume a lag between a new Laravel release and a compatible tag. Budget for the possibility of pinning Laravel one minor behind, or of a short vendor fork if a break lands mid-cycle. Because we are adopting a low-adoption dependency, assign a named owner for tracking upstream releases.

Support. Community-supported MIT project with a single primary maintainer; no vendor SLA. Internal support must come from our own docs: a short internal note specifying which models use which traits, which event classes exist, and the exact listener contract. The upstream docs are usable but demonstrably imperfect, so do not treat them as the source of truth — our tests should be.

Scaling. Event firing is per-operation and synchronous by default, so listener cost lands on the request path. Queue all non-trivial listeners (ShouldQueue) to keep write latency flat, and watch the two documented extra-query paths: detach/sync/toggle resolve affected IDs with an additional query, and update materializes a collection. In bulk sync operations (hundreds of IDs) this can turn a single statement into a fan-out of events and queries; enforce chunking and cap listener work per event. Under Octane, verify no static state accumulates in the relation subclasses.

Failure modes.

  • Silent no-op: trait not added to a model, or listener registered for the wrong relation type — nothing fires, no error. Mitigate with tests asserting each event fires.
  • Listener exception aborts the write inside the transaction, turning a logging concern into a user-facing 500. Mitigate with queued listeners and try/catch around non-critical side effects.
  • Duplicate/fan-out events: sync and toggle fire both the aggregate event and the underlying attach/detach events — listeners must be idempotent or explicitly scoped to one level.
  • Coverage gaps: mutations outside Eloquent relations (bulk insert, raw queries, DB::table pivot writes, imports) produce no events, so any audit trail built solely on this package is inherently incomplete.
  • Upgrade breakage: an Eloquent internals change silently stops events firing post-upgrade. Mitigate with a smoke test in CI keyed to the package version.
  • Ordering ambiguity: associating a morphTo/belongsTo marks the related model dirty and requires a subsequent save; listeners that act on the association may observe pre-save state.

Ramp-up. Very low for any Laravel developer — an hour to read the docs and mirror an existing model's pattern. The learning curve is not the API but the event-naming matrix (nine traits × creating/created/saving/saved/attaching/attached/detaching/detached/associating/associated/updating/updated). Mitigate with a one-page internal reference table and two or three canonical examples in the codebase rather than links to upstream docs. Estimated onboarding cost: half a day for one engineer to become the internal owner.

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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style