Skip to main content
skill CX Codex

eloquent-best-practices — Claude Code skill: what it does & setup

Provides guidelines for Laravel Eloquent ORM, from eager loading relationships to mass assignment guards. Useful for keeping queries fast and preventing N+1 problems.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add iSerter/laravel-claude-agents --skill eloquent-best-practices -a claude-code
Open on GitHub

What eloquent-best-practices does

The skill provides your coding agent with an Eloquent ORM reference guide. It directs the model to eager load relationships using with(), call withCount() instead of running counting queries inside loops, and retrieve only required columns via select(). This stops N+1 query bottlenecks before code reaches production.

It covers model structure, attributes, and migrations. The guidelines enforce explicit return types such as BelongsTo and HasMany, cast definitions via $casts, and foreign key indexing. For large datasets, it prioritizes chunk() or lazy() collections over loading entire tables into memory.

Setting protected $guarded = [] is explicitly flagged as unsafe due to mass assignment risks. The guide works best when paired with Model::preventLazyLoading() enabled in your local AppServiceProvider.

What it helps with

  • prevent N+1 query issues using eager loading
  • process large datasets with chunk and lazy collections
  • guard Laravel models against mass assignment vulnerabilities
  • enforce return types on Eloquent relationship methods

Example prompts for the agent

  • Review my Laravel models and queries for potential N+1 query issues.
  • Create a Post model with typed relations, attribute casts, and fillable fields.
  • Refactor this bulk update loop into a direct database-level query.

How to install eloquent-best-practices

1Skills CLI (Vercel)

npx skills add iSerter/laravel-claude-agents --skill eloquent-best-practices -a claude-code

2Manually: put the files in place

~/.claude/skills/eloquent-best-practices/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/eloquent-best-practices/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about eloquent-best-practices

How do I install eloquent-best-practices in Claude Code?

Run npx skills add iSerter/laravel-claude-agents --skill eloquent-best-practices -a claude-code in your terminal. You can also place the skill file directly at ~/.claude/skills/eloquent-best-practices/SKILL.md.

What does the eloquent-best-practices skill do?

It gives your assistant a checklist of best practices for writing performant Laravel Eloquent queries, configuring model guards, and indexing database migrations.

How does it address lazy loading during development?

It recommends adding Model::preventLazyLoading(!app()->isProduction()) inside the boot method of AppServiceProvider.

Source text

SKILL.md · MIT
---
name: eloquent-best-practices
description: Best practices for Laravel Eloquent ORM including query optimization, relationship management, and avoiding common pitfalls like N+1 queries.
---

# 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()->index();
    
    // Composite index for common queries
    $table->index(['status', 'published_at']);
});
```

### Prevent Lazy Loading in Development

```php
// In AppServiceProvider boot method
Model::preventLazyLoading(!app()->isProduction());
```

## Checklist

- [ ] Relationships eagerly loaded where needed
- [ ] Only selecting required columns
- [ ] Using query scopes for reusability
- [ ] Mass assignment protection configured
- [ ] Appropriate casts defined
- [ ] Indexes on foreign keys and query columns
- [ ] Using database-level operations when possible
- [ ] Chunking for large datasets
- [ ] Model events used appropriately
- [ ] Lazy loading prevented in development

© iSerter · MIT · iSerter/laravel-claude-agents

Similar skills

Everything for Backend and APIs →

From the same set2

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor