$ lexprog.com

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

[September 25, 2024] Eloquent ORM

Eloquent Belongs to Many: Pivot Data

Eloquent Belongs to Many: Pivot Data

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

Eloquent Belongs to Many: Pivot Data

Tip: Attach with Extra Data

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

Gotcha: Pivot Data Needs withPivot()

public function roles(): BelongsToMany
{
    return $this->belongsToMany(Role::class)->withPivot('expires_at');
}

Without withPivot(), extra columns are not accessible.

Tip: sync() for Full Replacement

$user->roles()->sync([1, 2, 3]);

Removes all existing roles and attaches only the listed ones.

Gotcha: sync() with Pivot Data

$user->roles()->sync([
    1 => ['expires_at' => now()->addYear()],
    2 => ['expires_at' => now()->addMonth()],
]);

Tip: toggle() for On/Off

$user->roles()->toggle([1, 2]);

Attaches if not present, detaches if present.

Gotcha: detach() Without Arguments

$user->roles()->detach();

Removes ALL role associations. Be careful.

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: whereHas() vs load() — Two Different Things

whereHas() filters the parent query by relationship existence. load() eager-loads relationships AFTER the query. Mixing them up is a common source of logic bugs.

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

Many-to-many relationships with pivot data are where Eloquent shows its limits. The default pivot model is read-only, and storing additional pivot data requires withPivot() plus either a custom pivot model or explicit attach() calls. I always define custom pivot models for relationships that carry meaningful data beyond the foreign keys — it makes the relationship explicit and the pivot data accessible naturally.

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

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