Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Eloquent Patterns

ASecurity

Eloquent ORM best practices: query builders, scopes, relations, N+1 prevention, batch operations, soft deletes, model events, raw queries when needed. Apply when: writing or reviewing Eloquent queries and model interactions. Activated automatically by laravel-plugin/stack.md as a convention skill for the development phase.

35 stars
0 votes
0 copies
0 views
Added 9/22/2026
code-qualityphpsqldatabaseperformance

Security Analysis

A100/100

Scanned 9/22/2026

Install to Claude Code

$npx -y skills add AratKruglik/claude-sdlc --skill eloquent-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Eloquent Patterns?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Eloquent Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aratkruglik-eloquent-patterns/badge)](https://www.skillsdirectory.com/skills/aratkruglik-eloquent-patterns)

More formats (shields.io, HTML) on the badges page.

Download with Pro
Files
SKILL.md
---
name: eloquent-patterns
description: |
  Eloquent ORM best practices: query builders, scopes, relations, N+1 prevention, batch operations, soft deletes, model events, raw queries when needed.
  Apply when: writing or reviewing Eloquent queries and model interactions.
  Activated automatically by laravel-plugin/stack.md as a convention skill for the development phase.
user-invocable: false
paths: ["app/Models/**", "database/**", "app/**/*.php"]
---

# Eloquent Patterns

Patterns for working with Eloquent that catch the common pitfalls (N+1, mass assignment, race conditions on counts, raw SQL injection).

## 1. N+1 prevention

The single most common Eloquent performance bug.

### Problem

```php
$users = User::all();
foreach ($users as $user) {
    echo $user->subscription->plan;  // 1 query per user → N+1
}
```

### Solution: eager loading

```php
$users = User::with('subscription')->get();
foreach ($users as $user) {
    echo $user->subscription?->plan;  // No extra queries
}
```

### Nested eager loading

```php
$users = User::with('subscription.invoices')->get();
```

### Selective columns (further optimization)

```php
$users = User::with(['subscription:id,user_id,plan,status'])->get();
```

### Detection in code review

Look for any `foreach` or `array_map` over an Eloquent collection followed by `->relation` access without `with()` upstream. That's N+1 90% of the time.

## 2. Scopes for reusable query logic

Encapsulate common query fragments as model scopes.

```php
class Subscription extends Model
{
    public function scopeActive(Builder $query): void
    {
        $query->where('status', 'active')
              ->where('ends_at', '>=', now());
    }

    public function scopeForUser(Builder $query, User $user): void
    {
        $query->where('user_id', $user->id);
    }
}

// Usage:
$activeForUser = Subscription::active()->forUser($user)->get();
```

Benefits:
- DRY — definition lives once.
- Testable — scopes can be unit-tested.
- Self-documenting — `Subscription::active()` reads better than `where('status', 'active')->where(...)`.

## 3. Mass assignment safety

```php
class Subscription extends Model
{
    protected $fillable = [
        'user_id',
        'plan',
        'status',
        'starts_at',
    ];
}
```

Then:

```php
Subscription::create($request->validated());  // ✅ safe
```

**Never:**

```php
class Subscription extends Model
{
    protected $guarded = [];  // ❌ — opens door to mass-assigning is_admin, etc.
}
```

## 4. Relations: declare return types

```php
public function user(): BelongsTo
{
    return $this->belongsTo(User::class);
}

public function invoices(): HasMany
{
    return $this->hasMany(Invoice::class);
}

public function paymentMethod(): MorphOne
{
    return $this->morphOne(PaymentMethod::class, 'payable');
}
```

Return types let the IDE / static analyzers understand the relation, and they catch errors at boot time, not runtime.

## 5. Counts and aggregates

### Lazy count (incurs a query each time)

```php
$count = $user->subscriptions()->count();  // 1 query
```

### Eager count

```php
$users = User::withCount('subscriptions')->get();
foreach ($users as $user) {
    echo $user->subscriptions_count;  // 0 extra queries
}
```

### Eager sum / avg / max

```php
$users = User::withSum('subscriptions', 'amount')
             ->withMax('invoices', 'created_at')
             ->get();
```

## 6. Batch operations

### Inserting many rows

```php
// Slow: triggers events per row
foreach ($rows as $row) {
    Model::create($row);
}

// Fast: single query, NO events fired
Model::insert($rows);

// Compromise: chunked + events
Model::query()->chunkById(500, function ($chunk) {
    // process
});
```

If you need events (timestamps, observers), use `Model::insert()` only when you've consciously accepted that timestamps and events won't fire.

### Updating many rows

```php
// One query, no events
Subscription::where('status', 'pending')
    ->where('created_at', '<', now()->subDays(7))
    ->update(['status' => 'expired']);

// Per-row with events (slower)
Subscription::where(...)->each(function ($s) {
    $s->update(['status' => 'expired']);
});
```

## 7. Soft deletes

```php
use Illuminate\Database\Eloquent\SoftDeletes;

class Subscription extends Model
{
    use SoftDeletes;
}
```

Implications:
- Migration must have `$table->softDeletes();`.
- Queries auto-exclude soft-deleted: `Subscription::all()`.
- To include them: `Subscription::withTrashed()->get()`.
- To get only soft-deleted: `Subscription::onlyTrashed()->get()`.
- To restore: `$subscription->restore()`.
- Force delete: `$subscription->forceDelete()`.

Watch out: relations don't auto-cascade soft deletes. Use observers or explicit logic.

## 8. Race conditions on counts/aggregates

Concurrent updates to a counter cause classic race bugs. Use atomic operations.

### Wrong

```php
$user = User::find(1);
$user->credits = $user->credits + 10;  // race
$user->save();
```

### Right (atomic via SQL)

```php
User::where('id', 1)->increment('credits', 10);
```

Or for a balance debit with overflow protection:

```php
$affected = User::where('id', 1)
    ->where('credits', '>=', 10)
    ->decrement('credits', 10);

if ($affected === 0) {
    throw new InsufficientCreditsException();
}
```

## 9. Raw queries — when and how

Sometimes Eloquent isn't enough (CTEs, window functions, complex aggregations). Use raw queries with bindings.

### Right

```php
DB::select(
    'SELECT * FROM subscriptions WHERE user_id = ? AND status = ?',
    [$userId, 'active']
);
```

### Wrong (SQL injection)

```php
DB::select("SELECT * FROM subscriptions WHERE user_id = $userId");  // ❌
DB::raw("user_id = $userId")  // ❌
```

### Right with `whereRaw` + bindings

```php
Subscription::whereRaw('amount > ?', [100])->get();
```

### Right with `selectRaw` for aggregates

```php
$stats = Subscription::query()
    ->selectRaw('plan, COUNT(*) as count, AVG(amount) as avg_amount')
    ->groupBy('plan')
    ->get();
```

## 10. Model events and observers

For cross-cutting behaviors (audit logs, cache invalidation, notifications), use observers — not boot methods inline.

```php
class SubscriptionObserver
{
    public function created(Subscription $subscription): void
    {
        AuditLog::record('subscription.created', $subscription);
    }

    public function deleting(Subscription $subscription): void
    {
        // Cleanup before deletion
    }
}
```

Register in `AppServiceProvider::boot()`:

```php
Subscription::observe(SubscriptionObserver::class);
```

## 11. Checklist before merging Eloquent changes

- [ ] No N+1: every `foreach` over an Eloquent collection has `with()` upstream
- [ ] `$fillable` set on every new/modified model
- [ ] All datetime/decimal columns in `$casts`
- [ ] No `DB::raw()` with string concatenation
- [ ] Atomic operations (`increment`, `decrement`) for counters under concurrency
- [ ] Soft deletes set up correctly (migration + trait) if used
- [ ] Observers used for cross-cutting concerns instead of model boot methods
- [ ] Eager-loaded relations used in views are declared in `with()` of the controller/action

Attribution

AratKruglikAratKruglik
View sourceMore from AratKruglik →
SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Related Skills

Caveman Commit

Ultra-compressed commit message generator. Cuts noise from commit messages while preserving intent and reasoning. Conventional Commits format. Subject ≤50 chars, body only when "why" isn't obvious. Use when user says "write a commit", "commit message", "generate commit", "/commit", or invokes /caveman-commit. Auto-triggers when staging changes.

1074701 votes

Caveman Review

Ultra-compressed code review comments. Cuts noise from PR feedback while preserving the actionable signal. Each comment is one line: location, problem, fix. Use when user says "review this PR", "code review", "review the diff", "/review", or invokes /caveman-review. Auto-triggers when reviewing pull requests.

1074701 votes

Springboot Verification

Verification loop for Spring Boot projects: build, static analysis, tests with coverage, security scans, and diff review before release or PR.

2456590 votes

Verification Loop

一个全面的 Claude Code 会话验证系统。

2456590 votes

Django Verification

Verification loop for Django projects: migrations, linting, tests with coverage, security scans, and deployment readiness checks before release or PR.

2456590 votes
View all in code-quality →