audunru/eager-load-pivot-relations
Eager load relations on pivot models in Laravel Eloquent BelongsToMany relationships. Add the EagerLoadPivotTrait to the related model to load nested pivot data such as units or quantities. Supports Laravel 8 and above.
qty, price, and unit_id; an order line carries a discount code; a team membership carries a role. This package makes the cheap modeling option (keep the pivot, add FKs and relations to it) viable in production, because you can still hydrate those relations without an N+1 storm. That lets you defer a costly schema refactor to a first-class LineItem model until the domain actually demands it.Plan::with('items.pivot.unit') collapses what would otherwise be one query per row.EagerLoadPivotTrait) that overrides newBelongsToMany(). There is no service provider, facade, config, migration, or schema change. Buying costs one Composer line; building costs a day plus ongoing ownership of Eloquent internals knowledge. Adopting is the obviously cheaper option if the domain fits.belongsToMany relations need pivot-relation hydration. It does not become a codebase-wide idiom, doesn't constrain the rest of the team's Eloquent usage, and is reversible per-model.items.pivot.unit.someRelation and nested pivot chains (...pivot.unit.someBelongsToManyRelation.pivot.otherRelation) become a shared contract between backend and API consumers. If you document these shapes, they become part of your public API surface — worth a deliberate naming decision (->as('planItem') vs. the default pivot) so payload keys read well to consumers.Adopt when all of the following hold:
belongsToMany relation is backed by a custom pivot model (->using(...)) that itself declares Eloquent relations — typically a foreign key to a lookup/reference entity (unit, currency, role, price tier, supplier).Look elsewhere — or don't adopt — when:
qty and price, Laravel's built-in ->withPivot(...) already solves it and this package adds nothing.hasMany/belongsTo and use ->through() or explicit queries. That's the right long-term shape, and the package would just be postponing it.ajcastro/eager-load-pivot-relations, itself by the same original author) maintained by a single person, with no downstream ecosystem to catch regressions. Some teams will reasonably prefer to vendor the class into app/ under their own test coverage.If you do adopt with the risk above in mind, the mitigation is cheap: the package is small enough to read in full, so pin the version, add one integration test asserting the eager-load shape and query count, and treat "vendor it into the app namespace" as a live fallback rather than an emergency.
For executives (one paragraph): We have a small number of screens and exports where each row needs a detail from a lookup table — for example, each line of a plan or order needs its unit of measure or supplier. Today those produce one database query per row, which is invisible at demo volume but degrades linearly as customers add data. This package is a one-line addition that lets us load those details in a single batch instead of one-by-one, protecting response times as data grows. It requires no database changes, no migration, no infrastructure work, and no downtime; it touches only the handful of models that need it, and it can be removed as cleanly as it was added. Cost is effectively zero engineering days; the risk is a very small, single-maintainer open-source dependency, which we contain by pinning the version and covering the behavior with our own test.
For engineering (the short version): Add EagerLoadPivotTrait to the model that declares a belongsToMany relation; it overrides newBelongsToMany() to return a subclass that understands a pivot (or custom ->as() accessor) segment in eager-load strings. Plan::with('items.pivot.unit') then hydrates pivot relations in batch. Pivot classes stay plain Pivot subclasses — no changes needed to them. No service provider, no config, no migrations. Caveats to plan around: the trait must be on the model that defines the relation (for custom accessors, on the parent side too); required pivot FKs must be listed in ->withPivot(); and the package's major versions track Laravel majors with a rising PHP floor (PHP 8.3 for Laravel 13), so it joins your framework-upgrade checklist. It is a single-maintainer fork-of-a-fork with negligible adoption, so: pin it, add one test that asserts the load shape and the query count, and keep "copy the trait into app/" as a known, low-effort exit.
Recommendation: a timeboxed spike — instrument one real list endpoint, measure query count before and after — then adopt per-model where the numbers justify it. This is a tool for buying time on a data-model decision, not a substitute for making it.
How can I help you explore Laravel packages today?