agentsclimarketplace

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

Install
npx -y skills add kwhorne/elyra-skills --skill laravel-eloquent-performance

Assembled 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 by with()
  • Big text/blob columns excluded from list queries
  • exists() not count() > 0; value('col') not first()->col

4. Move work to the database

PHP smellDatabase 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() (not chunk() when mutating rows) or lazyById() / 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 — use chunkById())
  • ❌ 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.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.