staudenmeir/eloquent-has-many-deep
Extended HasManyThrough for Laravel 5.5+ lets you define relationships with unlimited intermediate models, supporting many-to-many, polymorphic, and combined relationship types. Define deep relationships by concatenating existing ones or specifying keys manually; includes third-party package support...
Architecture fit. The package is additive rather than invasive: it is a trait (HasRelationships) plus relationship classes (HasManyDeep, HasOneDeep) that extend Eloquent's relationship contract. There is no service provider, no config file, no migration, no schema change, and no query-layer replacement. It slots into the existing Eloquent relationship graph, which means it inherits the framework's conventions for eager loading (with()), constraints (whereHas), serialization, and soft deletes. For any application already modeling data as normalized relational tables with Eloquent models — the dominant Laravel architecture — this is a near-zero-friction fit.
Integration feasibility. Effort is small and bounded: one Composer dependency, one trait per participating model, one relationship method per traversal path. Two definition modes lower the bar further — concatenating existing relationships (hasManyDeepFromRelations) is declarative and reuses relationships already reviewed by the team, while the manual mode (hasManyDeep(Related::class, [IntermediateA, IntermediateB], $foreignKeys, $localKeys)) covers paths where those relationships don't exist. The feature surface is unusually complete for a utility package: many-to-many, all four polymorphic variants, BelongsTo hops, composite keys, pivot/intermediate data access, table aliases, and soft-delete handling. Quality signals are strong: CI, Codecov, PHPStan level 10, and a maintained version matrix mapping Laravel 5.5→13 to specific package majors. That matrix is also the primary compatibility contract — it tells us precisely which Laravel upgrade requires which package bump.
Technical risk. The risk is concentrated in generated SQL, not in integration:
whereHas over a deep relationship is a correlated subquery that can be substantially slower than an equivalent hand-written join.distinct()/unique handling is applied. This is a correctness-adjacent footgun that shows up as pagination count drift, not an exception.Key questions.
EXPLAIN discipline) for the endpoints we adopt this on?Stack fit. Any PHP/Laravel service on MySQL, Postgres, SQL Server, or SQLite, using Composer and Eloquent. It composes with adjacent packages in the same author's ecosystem (eloquent-json-relations, laravel-adjacency-list) and with korridor/laravel-has-many-merged and topclaudy/compoships — relevant only if we already use those. If our data access is largely raw query builder or repository-pattern with hand-written SQL, the value proposition drops sharply.
Migration path. Greenfield usage is a non-event. Brownfield, the realistic path is replacing three recurring patterns: (a) hand-rolled multi-join DB::table(...) queries embedded in controllers or repositories, (b) chains of whereHas over intermediate models to express "descendants of X", and (c) ad-hoc ID-plucking queries (Model::whereIn('id', $ids)->get()) that produce N+1-adjacent code. Each replacement is independently shippable and reversible — the package adds capability without removing the old code path, so a per-endpoint cutover with a feature flag is straightforward. Database views are the main incumbent; they remain a valid fallback for the heaviest reporting queries, and we should not force a migration where a view already performs acceptably.
Compatibility. Pin the package major to the Laravel major (e.g., Laravel 13 → 1.22) and encode that in composer.json constraints so a framework bump cannot silently outrun the package. Verify PHP version support against our runtime. Check for local Eloquent extensions — custom Builder subclasses, global scopes on intermediate models, or tenancy packages that rewrite connections — since those interact with the generated queries. Confirm behavior with our soft-delete conventions (models using SoftDeletes in the intermediate path) and with any table-prefix configuration.
Sequencing.
EXPLAIN output captured on production-shaped data.distinct/unique-result handling where fan-out exists.Maintenance. Ongoing cost is close to the floor for a third-party dependency: no runtime services, no data to migrate, no configuration to drift. The real maintenance obligation is upgrade choreography — tracking the Laravel-to-package version matrix at each framework upgrade, and confirming a compatible release exists before committing to a Laravel major. Budget roughly an hour per Laravel upgrade cycle to verify, plus whatever remediation is needed if the package lags.
Support. MIT, community-supported, no vendor SLA, no commercial backing. Issue resolution depends on the maintainer's availability; the codebase's PHPStan level 10 and CI/coverage posture suggests changes are reviewable, and the MIT license means an internal fork is a legitimate contingency with a defined owner. For a dependency confined to read-path query construction, that risk profile is acceptable. It would not be acceptable if the package sat on a revenue-critical hot path with no fallback — which argues for keeping the hand-written alternative for the two or three most performance-sensitive queries.
Scaling. This is the dimension that needs active management. Deep relationships scale with the product of the joined tables' selectivity, not with any single index. Practical guardrails:
with()) deep relationships in list contexts; lazy-loading inside a loop turns one deep query into N.with() on a paginated parent.Failure modes. Ranked by likelihood and blast radius:
EXPLAIN on production-shaped data at pilot and a latency alert on the endpoint.Ramp-up. A Laravel-fluent engineer is productive with the concatenation API in under an hour; the manual key-array API realistically takes a day of hands-on work to internalize the ordering and the pivot key-swap rule. Plan for one engineer to own the pattern, author the ADR and the first two relationships, and review subsequent additions — this is a "one person establishes the convention, everyone else copies it" dependency, not a broad training program. Total adoption cost for a focused pilot and three production relationships is on the order of one developer-week, which against the alternative of hand-maintaining multi-join SQL across several endpoints is a favorable trade provided step 4's decision gate is honored.
How can I help you explore Laravel packages today?