atldays/laravel-eloquent-join-relation
Add SQL joins to Laravel Eloquent relationships without dropping to the query builder. atldays/laravel-eloquent-join-relation lets you join related tables through existing relationship definitions, filter and sort by joined columns, and keep clean, readable model code.
join related tables to filter or sort (e.g., sort users by teams.name, filter orders by customers.country). Today those endpoints either drop the relation from the payload or pay for a second query. This package lets the same query return hydrated BelongsTo / HasOne models, so the performance win and the response shape aren't in conflict.with(['author' => fn ($q) => ...])) plus a second round trip, or (c) reshaping the query to avoid joins. A small, single-purpose package replaces (a) — which is the option that accretes the most bespoke, untested code — and beats (b) on query count. It does not replace general eager loading; it's a targeted tool.BelongsTo and HasOne only, with collection and polymorphic relations explicitly out of scope. That maps to a clear adoption boundary — use it for the "detail row" half of your API surface now, and treat HasMany/MorphTo hydration as a dependency to track, not something to plan around.null on left join, the package makes the "related record absent" case expressible in the payload without a follow-up query. That's a direct input to response-schema and nullability decisions for consumer teams.author.team.organization resolved in one round trip.HasMany, ManyToMany, or polymorphic relations loaded from the same join; write paths; queries where the join is incidental and plain with() would do.Adopt when most of these hold:
join for filtering, sorting, or validation, and the related data is therefore already in the result set — you're paying for the join and currently throwing the columns away.BelongsTo or HasOne, including nested paths (author.team.organization).Look elsewhere, or wait, when:
HasMany, ManyToMany, or polymorphic relation hydration — explicitly out of scope for this release, and the fallback-lazy-load protection means you'll get an explicit failure rather than silent degradation, which is good but still a blocker.with() plus a second query is acceptable — often it is, and it's the lower-risk, zero-dependency choice.Adoption caveats to carry into review: the package's correctness depends on the join shape matching the declared relation path — misdeclared or reordered nested paths are rejected with explicit exceptions rather than guessed at, so expect to spend a little time on query construction. Existing test coverage is described for advanced multi-join scenarios, which is the right place to look; ask for the repo and verify the left join / null path and the lazy-load guard against your own fixtures before committing.
For executives (one paragraph): We're evaluating a small Laravel package that removes a common source of database overhead — the extra queries we fire to load related records on list and search screens. It works with queries we already write, so it's an efficiency change rather than a re-architecture: fewer round trips per page load, less custom glue code for engineers to maintain, and no change to the API shape our clients see. Scope is intentionally limited to the most common relation types, on our current PHP and Laravel versions. Risk is low and contained — it's additive, it fails loudly rather than silently when a relation isn't supported, and we can pilot it on one or two high-traffic endpoints before deciding.
For engineering leads: This is the "we already joined, so use it" case. It hydrates BelongsTo and HasOne — including nested paths like author.team.organization — directly from the joined row set, with left join producing null for missing records and nested paths required in declared order (enforced by explicit exceptions). Fallback lazy loading is blocked, so unsupported or misshapen usage surfaces immediately instead of quietly costing us queries. Requires PHP 8.2+, Laravel 11–13. Not a fit for HasMany or polymorphic relations in this release. Proposed next step: pilot on one paginated endpoint that sorts on a related column, confirm query-count reduction and payload parity, then decide on broader use.
How can I help you explore Laravel packages today?