Laravel eloquent performance
Skill kwhorne/elyra-skills/skills/laravel-eloquent-performance
Find and fix Eloquent performance problems - N+1 queries, eager loading strategy, chunking, indexes, and query review for Laravel apps. Use when a Laravel page or job is slow, when reviewing Eloquent code for query efficiency, when the user mentions N+1 problems, or before shipping list views and reports that touch large tables.From its SKILL.md
npx -y skills add kwhorne/elyra-skills --skill laravel-eloquent-performanceAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
4.5 KB, ~1.0k tokens by cl100k_base, as published. Nobody here has run it
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 <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: <page/endpoint/job>
**Baseline:** N queries / X ms → **After:** M queries / Y ms
### Fixes
1. <file:line> — <problem> → <fix>
### Indexes added/proposed
- <table>.<cols> — for <query>
### 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
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most performance cost skills give in ~1.0k tokens
Counted across 803 of the 1,058 authors here whose files we hold, read 2026-08-07
- Keep skill files under 500 lines or tokensin 82 of 803, across 16 files
- Use imperative form in instructionsin 80 of 803, across 9 files
- Draft assertions while test runs are in progressin 75 of 803, across 9 files
- Create two to three realistic test promptsin 74 of 803, across 9 files
- Write skill descriptions to be pushyin 72 of 803, across 7 files
- Save test cases to evals JSONin 72 of 803, across 6 files
- Ask questions about edge cases and input formatsin 72 of 803, across 7 files
- Save timing data immediately when runs completein 70 of 803, across 5 files
- Include all trigger conditions in the skill descriptionin 69 of 803, across 3 files
- Launch all test runs in a single turn or simultaneouslyin 69 of 803, across 3 files
- Capture intent before writing a skillin 67 of 803, across 1 file
- Import directly instead of barrel filesin 52 of 803, across 15 files
Said here and by no other author read
- measure queries and duration before fixing
- enable Eloquent strict mode in non-production
- use database aggregates instead of PHP collection methods
- verify added indexes are used by EXPLAIN
- add CI tests asserting maximum query counts
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.