Install
$ agentstack add skill-kwhorne-elyra-skills-laravel-eloquent-performance ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Laravel Eloquent Performance
Eloquent makes queries invisible — which is how one Blade loop quietly becomes 300 of them. Make queries visible first, then fix the worst offender, then prevent recurrence.
When to use
- A Laravel page, endpoint, or job is slow
- Reviewing code that loops over models or renders collections
- "We have an N+1 problem" / preparing list views, exports, dashboards
- Before shipping anything that touches large tables
Principles
- Measure before touching. Count queries and duration; don't guess from code shape.
- The database does sets; PHP does rows. Filtering, counting, and aggregating in a
->filter()/->count()after->get()is the cardinal sin. - Load what the view needs, nothing more. Eager load relations the page renders; select the columns it shows.
- Prevention beats heroics. One strict-mode line catches future N+1s in development forever.
Process
1. Make queries visible
// AppServiceProvider::boot() — development
Model::shouldBeStrict(! app()->isProduction()); // throws on lazy loading, silent attribute access
DB::listen(fn ($q) => logger()->debug($q->sql, ['ms' => $q->time]));
Or use Debugbar/Telescope/->dd() on the query log for the specific request. Record: query count + total ms as the baseline.
2. Fix N+1 (the usual suspect)
// Symptom: query per row in a loop
$posts = Post::with(['author', 'comments.user'])->get(); // eager load
$posts = Post::withCount('comments')->get(); // counts without loading
$query->withAvg('reviews', 'rating'); // aggregates likewise
- Nested relations:
with('comments.user'), constrained:with(['comments' => fn ($q) => $q->latest()->limit(5)]) - In Blade components rendered per-row: the N+1 hides in the component — eager load in the parent query
3. Stop over-fetching
->select(['id', 'title', 'author_id'])— include FKs needed bywith()- Big text/blob columns excluded from list queries
exists()notcount() > 0;value('col')notfirst()->col
4. Move work to the database
| PHP smell | Database fix | |---|---| | ->get()->filter(...) | ->where(...) | | ->get()->count() | ->count() | | ->get()->sum('x') | ->sum('x') | | ->get()->groupBy(...) | ->groupBy() + aggregate, or a dedicated query |
5. Handle large datasets
- Iteration:
chunkById()(notchunk()when mutating rows) orlazyById()/cursor() - Exports/jobs: never
all()on an unbounded table - Pagination:
paginate()for UI;cursorPaginate()for infinite scroll / large offsets
6. Check indexes
php artisan db:table # see existing indexes
- Every column in
where/orderBy/joins on big tables: candidate - Composite index order: equality columns first, then range/sort
- Verify with
DB::select('EXPLAIN ...')— look for full table scans
7. Re-measure and lock in
- Compare query count + ms against baseline; report both
- Keep
Model::shouldBeStrict()in non-production permanently - Add a CI-friendly assertion where it matters: e.g. test that a page renders in ≤ N queries
Output format
## Eloquent performance:
**Baseline:** N queries / X ms → **After:** M queries / Y ms
### Fixes
1. — →
### Indexes added/proposed
- . — for
### Prevention
- strict mode: on | query-count test: added | …
Anti-patterns
- ❌ Sprinkling
with()everywhere "just in case" — eager loading unrendered relations is its own waste - ❌ Caching as the first response to a slow query instead of fixing the query
- ❌
chunk()while updating the column you chunk by (rows get skipped — usechunkById()) - ❌ Fixing the N+1 in the controller while the Blade component re-introduces it
- ❌ Adding indexes without checking they're used (
EXPLAIN) - ❌ Declaring victory without before/after numbers
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kwhorne
- Source: kwhorne/elyra-skills
- License: MIT
- Homepage: https://elyracode.com
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.