$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[March 02, 2026] Eloquent ORM

Eloquent Relationships: The Complete Guide

Eloquent Relationships: Tips & Tricks

────────────────────────────────────────────────────────

Eloquent Relationships: Tips & Tricks

Tip: Always Eager Load in Loops

// Bad: N+1 queries
foreach ($posts as $post) {
    echo $post->author->name;
}

// Good: 2 queries total
$posts = Post::with('author')->get();

Gotcha: with() Loads All Related Records

Post::with('comments')->get();
// Loads ALL comments for ALL posts

Use constraints:

Post::with(['comments' => fn($q) => $q->latest()->limit(5)])->get();

Tip: Use exists() Instead of count()

// Bad: loads all records
if ($user->posts->count() > 0) { }

// Good: single EXISTS query
if ($user->posts()->exists()) { }

Tip: whereHas() for Filtering by Relation

Post::whereHas('author', fn($q) => $q->where('active', true))->get();

Gotcha: BelongsToMany Needs a Pivot Table

The table must have both foreign keys. Convention: singular model names alphabetical.

post_user: post_id, user_id

Tip: Access Pivot Data

$user->roles()->attach($roleId, ['expires_at' => now()->addYear()]);

// Access:
$role->pivot->expires_at;

Tip: has() vs whereHas()

Post::has('comments')->get();           // Posts with any comments
Post::whereHas('comments', fn($q) => $q->where('approved', true))->get(); // Posts with approved comments

Gotcha: Polymorphic Relations Can't Use Foreign Keys

No database-level integrity. The related model could be deleted without cleaning up the polymorphic reference.

Tip: Use cursor() for Memory-Neutral Iteration

When exporting 100K rows, get() loads everything into memory. cursor() uses yield and keeps memory flat regardless of row count. Perfect for artisan commands.

Tip: Pivot Data Is Read-Only by Default

Many-to-many pivot columns are read-only unless you define custom pivot models. Use ->withPivot('expires_at') to make them accessible, and newPivot() to make them writable.

Gotcha: withCount() Adds a Subquery

withCount('comments') runs a correlated subquery on every row. On large tables, this can be slower than a separate query. Profile before relying on it.

Senior Insight

Eloquent is Laravel's most controversial feature — developers either love its convenience or hate its magic. My take after years of using both Eloquent and raw SQL: Eloquent is excellent for 80% of queries and wrong for the other 20%. The skill is knowing which is which. If a query involves more than three joins, window functions, or complex subqueries, reach for the Query Builder or raw SQL. Eloquent's beauty is in its simplicity for CRUD; fighting it for complex queries helps no one.

Source: Laravel Docs (https://laravel.com/docs/eloquent), Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/eloquent)

────────────────────────────────────────────────────────
<-- back to posts