staudenmeir/eloquent-eager-limit
Add per-parent limits to eager-loaded Eloquent relationships in Laravel. Load only the latest N related models for each parent (e.g., newest comments per post) without N+1 queries. Supports common relations and integrates cleanly with queries and pagination.
Installation
composer require staudenmeir/eloquent-eager-limit
No additional configuration is required—it integrates automatically with Eloquent.
First Use Case: Limited Eager Loading Fetch only the 3 most recent comments per blog post:
$posts = Post::with(['comments' => function ($query) {
$query->orderBy('created_at', 'desc')->limit(3);
}])->get();
Replace with the package’s syntax:
$posts = Post::withLimit(['comments' => 3], 'created_at', 'desc')->get();
Where to Look First
hasMany, belongsToMany).Basic Limited Eager Loading
// Fetch 2 latest replies per thread
$threads = Thread::withLimit(['replies' => 2], 'created_at', 'desc')->get();
Nested Relationships
// Fetch 1 top comment + 1 top reply per comment (max 2 replies)
$posts = Post::withLimit([
'comments' => 1,
'comments.replies' => 2
], 'created_at', 'desc')->get();
Dynamic Limits via Closures
$posts = Post::withLimit(function ($query) {
return $query->limit(5)->where('published', true);
})->get();
Combining with Other Query Methods
// Paginate posts with limited comments
$posts = Post::withLimit(['comments' => 3])
->paginate(10)
->appends(['comments_limit' => 3]);
Offset Support (for Pagination)
// Skip 2 oldest comments, fetch next 3
$posts = Post::withLimit(['comments' => [3, 2]], 'created_at', 'asc')->get();
withLimit to control payload size for endpoints like /posts/{id}/comments.users.orders.items) to avoid overwhelming dashboards.remember():
$posts = Post::withLimit(['comments' => 5])
->remember(60)
->get();
Database-Specific Quirks
TOP syntax adjustments (package handles this automatically).LIMIT in subqueries.LIMIT in subqueries.Ordering Without a Column
withLimit(['comments' => 3]) without an order column throws an exception.orderBy:
withLimit(['comments' => 3], 'created_at', 'desc')
Nested Limits and Performance
withLimit (e.g., A.B.C.D) can bloat queries.Offset Misuse
[limit, offset] syntax is per parent, not global. For global offsets, use raw queries or cursor().Polymorphic Relations
$posts = Post::withLimit(['comments' => 2])->get();
foreach ($posts as $post) {
$post->comments = $post->comments->sortBy('created_at');
}
\DB::enableQueryLog();
$posts = Post::withLimit(['comments' => 3])->get();
dd(\DB::getQueryLog());
Column not found: Ensure the orderBy column exists in the related table.Syntax error: Check for unsupported database syntax (e.g., SQLite’s LIMIT in subqueries).Custom Drivers
Override the LimitClause logic in staudenmeir/eloquent-eager-limit/src/LimitClause.php for unsupported databases.
Dynamic Limits via Observers/Events
Post::observe(function ($post) {
if (auth()->check()) {
$post->loadWithLimit(['comments' => 10]);
}
});
Macros for Reusability
use Staudenmeir\EloquentEagerLimit\LimitClause;
EloquentBuilder::macro('withLatestComments', function ($limit = 5) {
return $this->withLimit(['comments' => $limit], 'created_at', 'desc');
});
Usage:
$posts = Post::withLatestComments()->get();
Testing
Mock the LimitClause in PHPUnit:
$this->partialMock(LimitClause::class, function ($mock) {
$mock->shouldReceive('getQuery')->andReturn($query);
});
```markdown
### Configuration Quirks
- **No Config File**: The package auto-detects database drivers; no `config/eloquent-eager-limit.php` exists.
- **Global Defaults**: Not supported—set limits per query or via macros.
- **Pivot Tables**: Works with `belongsToMany`, but ensure the pivot has an orderable column (e.g., `created_at`).
How can I help you explore Laravel packages today?