kingmaker/laravel-many-to-many-self-relationship
A Laravel Eloquent package for many-to-many self-referential relationships like followers/following, friends, or related models. It provides a reusable trait for managing pivot records, inverse relations, and attach/detach-style operations cleanly.
Architecture fit. The package targets a narrow but real Eloquent gap: self-referential many-to-many (user↔user friendships/follows, product↔related-product, category↔related-category). Laravel's native belongsToMany(static::class, ...) already supports this asymmetrically; the package's differentiator is two-way association — attaching A→B also makes B→A queryable without a second query or manual reverse lookup. That is a legitimate ergonomic win, but it is a thin layer over framework primitives rather than a new capability, which caps its strategic value.
Integration feasibility. Low-friction on paper: Composer install, add a trait to the model, define the relationship method, create a pivot table. No infrastructural dependencies. The real feasibility gate is Laravel/PHP version compatibility and whether the pivot-duplication strategy matches our data conventions — both unverified given the package's maturity signals (18 stars, 0 dependents, no downstream usage to cite as proof).
Technical risk. High for the value delivered:
attach/detach/sync, unique-constraint collisions, transaction boundaries) or a single-row plus symmetric query scope. Each choice has distinct correctness and performance trade-offs, and partial writes are the classic failure mode.detach leaving a stale inverse, soft deletes, custom pivot models, polymorphic pivots, and eager-loading behavior.Key questions.
detach, sync, and cascade delete?wherePivot, custom pivot classes, soft deletes, and withCount?Stack fit. Clean fit for a conventional Laravel/Eloquent codebase with an existing relational store. It is not appropriate if we run a graph-heavy domain at scale (large follow graphs, recommendation traversals) where an adjacency-list or dedicated graph store would be the correct primitive. Treat it as a convenience layer for bounded, moderate-cardinality relations — "related articles," "friends within a tenant" — not as a social-graph engine.
Migration path. Greenfield adoption is trivial: install, add trait, write pivot migration with a composite unique index on (model_id, related_model_id). Brownfield is where cost lives — if we already have asymmetric belongsToMany data, we need a backfill that inserts inverse rows, deduplicates, and reconciles orphaned pivots, plus a reversible rollback plan. Budget that as a data-migration task, not a package-install task.
Compatibility. Pin the package version and verify against our actual Laravel release in a scratch branch before committing. Because there are no dependents, we cannot borrow someone else's compatibility evidence; we must generate it. If the constraints do not cover our version, that is a stop signal, not something to patch around.
Sequencing.
attach/detach/sync symmetry plus duplicate and concurrency behavior.belongsToMany relations plus a symmetry observer, so we are never locked in.Maintenance. The dominant cost is not the code we write but the code we inherit. Assume we are the de facto maintainers: vet the source, fork it internally if adopted, and add our own test coverage around the symmetry invariant. Track it in the dependency register with an explicit owner and a review date.
Support. Community-only, effectively single-maintainer. No SLA, no vendor escalation path. Any production defect becomes our incident with our own remediation clock.
Scaling. The pivot strategy determines the ceiling. Duplicate-row symmetry doubles write volume and table size and makes hot-path writes more contention-prone on the unique index; symmetric queries need well-chosen composite indexes or they degrade to full scans. For high-fan-out entities, validate query plans under realistic row counts before launch rather than after.
Failure modes.
detach, producing one-directional visibility bugs that are hard to reproduce.attach of the same pair.Ramp-up. Low for any developer fluent in Eloquent — an hour to productivity. The non-obvious cost is conceptual: the team must internalize that the relationship is bidirectional and that writes are not symmetric-by-default at the database layer. Document the invariant, add a lint/CI assertion, and require review on any code that touches the pivot.
Recommendation. Adopt only if the spike confirms version compatibility and clean symmetry semantics, and only for bounded-domain relations. Given the low maturity signals, prefer a thin internal implementation over the package unless the spike shows the package saves meaningfully more than a day of work — and even then, fork it under our control.
How can I help you explore Laravel packages today?