
Eloquent Best Practices
Apply Laravel Eloquent patterns so solo builders ship faster APIs and admin backends without N+1 slowdowns or bloated queries.
Overview
Eloquent Best Practices is an agent skill for the Build phase that guides Laravel Eloquent query optimization, relationship loading, and scoped query patterns.
Install
npx skills add https://github.com/iserter/laravel-claude-agents --skill eloquent-best-practicesWhat is this skill?
- Eager-load relationships with `with()` to eliminate classic N+1 query loops
- Select only required columns on models and constrained relationship selects
- Reusable query scopes (`scopePublished`, `scopePopular`) for readable filters
- Typed relationship methods (`BelongsTo`, `HasMany`) for safer model APIs
- Side-by-side anti-pattern vs recommended PHP snippets for copy-paste guidance
Adoption & trust: 1k installs on skills.sh; 40 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your Laravel feature works but pages or jobs trigger N+1 queries and pull entire tables because relationships and selects were not designed with Eloquent best practices.
Who is it for?
Solo builders or small teams shipping Laravel SaaS/API backends who want agent-generated Eloquent code aligned with performance-minded conventions.
Skip if: Greenfield projects using raw SQL or a non-Laravel stack, or teams that already enforce strict repository layers with query analysis in CI.
When should I use this skill?
When writing or reviewing Laravel Eloquent models, queries, relationships, and scopes to avoid N+1 queries and inefficient fetches.
What do I get? / Deliverables
After the skill runs, models and queries use eager loading, tight selects, and scopes so read paths stay predictable and easier for agents to extend safely.
- Refactored Eloquent queries with eager loading and scopes
- Relationship methods with explicit return types
Recommended Skills
Journey fit
Eloquent modeling and query tuning happen while implementing server-side persistence and domain logic in Laravel apps. The skill is anchored on ORM queries, relationships, and scopes—core backend data-layer work, not frontend or DevOps.
How it compares
Use as a procedural Laravel ORM checklist during implementation, not as a generic SQL tutor or database migration generator.
Common Questions / FAQ
Who is eloquent-best-practices for?
PHP Laravel solo builders and indies who use AI coding agents to implement models, APIs, and admin features and want Eloquent code that avoids common performance traps.
When should I use eloquent-best-practices?
During Build when adding relationships, list endpoints, or scopes; also when refactoring controllers or resources that loop over models and hit the database repeatedly.
Is eloquent-best-practices safe to install?
It is documentation-style guidance without mandated shell or network access; review the Security Audits panel on this Prism page before adding it to your agent workflow.
SKILL.md
READMESKILL.md - Eloquent Best Practices
# Eloquent Best Practices ## Query Optimization ### Always Eager Load Relationships ```php // ❌ N+1 Query Problem $posts = Post::all(); foreach ($posts as $post) { echo $post->user->name; // N additional queries } // ✅ Eager Loading $posts = Post::with('user')->get(); foreach ($posts as $post) { echo $post->user->name; // No additional queries } ``` ### Select Only Needed Columns ```php // ❌ Fetches all columns $users = User::all(); // ✅ Only needed columns $users = User::select(['id', 'name', 'email'])->get(); // ✅ With relationships $posts = Post::with(['user:id,name'])->select(['id', 'title', 'user_id'])->get(); ``` ### Use Query Scopes ```php // ✅ Define reusable query logic class Post extends Model { public function scopePublished($query) { return $query->where('status', 'published') ->whereNotNull('published_at'); } public function scopePopular($query, $threshold = 100) { return $query->where('views', '>', $threshold); } } // Usage $posts = Post::published()->popular()->get(); ``` ## Relationship Best Practices ### Define Return Types ```php use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; class Post extends Model { public function user(): BelongsTo { return $this->belongsTo(User::class); } public function comments(): HasMany { return $this->hasMany(Comment::class); } } ``` ### Use withCount for Counts ```php // ❌ Triggers additional queries foreach ($posts as $post) { echo $post->comments()->count(); } // ✅ Load counts efficiently $posts = Post::withCount('comments')->get(); foreach ($posts as $post) { echo $post->comments_count; } ``` ## Mass Assignment Protection ```php class Post extends Model { // ✅ Whitelist fillable attributes protected $fillable = ['title', 'content', 'status']; // Or blacklist guarded attributes protected $guarded = ['id', 'user_id']; // ❌ Never do this // protected $guarded = []; } ``` ## Use Casts for Type Safety ```php class Post extends Model { protected $casts = [ 'published_at' => 'datetime', 'metadata' => 'array', 'is_featured' => 'boolean', 'views' => 'integer', ]; } ``` ## Chunking for Large Datasets ```php // ✅ Process in chunks to save memory Post::chunk(200, function ($posts) { foreach ($posts as $post) { // Process each post } }); // ✅ Or use lazy collections Post::lazy()->each(function ($post) { // Process one at a time }); ``` ## Database-Level Operations ```php // ❌ Slow - loads into memory first $posts = Post::where('status', 'draft')->get(); foreach ($posts as $post) { $post->update(['status' => 'archived']); } // ✅ Fast - single query Post::where('status', 'draft')->update(['status' => 'archived']); // ✅ Increment/decrement Post::where('id', $id)->increment('views'); ``` ## Use Model Events Wisely ```php class Post extends Model { protected static function booted() { static::creating(function ($post) { $post->slug = Str::slug($post->title); }); static::deleting(function ($post) { $post->comments()->delete(); }); } } ``` ## Common Pitfalls to Avoid ### Don't Query in Loops ```php // ❌ Bad foreach ($userIds as $id) { $user = User::find($id); } // ✅ Good $users = User::whereIn('id', $userIds)->get(); ``` ### Don't Forget Indexes ```php // Migration Schema::create('posts', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->index(); $table->string('slug')->unique(); $table->string('status')->index(); $table->timestamp('published_at')->nullable