Reader Stacks

Eager Loading Specific Columns With Laravel's with() Method

Loading only the columns a relationship actually needs, rather than every column, is a genuine performance win — with one easy-to-miss requirement: the foreign key must always be included.

Eager Loading Specific Columns With Laravel's with() Method

Eager loading a relationship with with() avoids the N+1 query problem, but by default it fetches every column of the related model — selecting only the specific columns actually needed is a genuine performance improvement, with one easy-to-overlook requirement.

Basic eager loading, fetching every column

$posts = Post::with('author')->get();

Eager loading with specific columns only

$posts = Post::with('author:id,name,email')->get();

Listing specific columns after a colon restricts the eager-loaded relationship to only those fields — genuinely useful when the related model has many columns (a large users table with dozens of fields) but only a couple are actually needed for the current view.

The essential requirement: always include the foreign key

// WRONG — missing the foreign key needed to actually match the relationship
$posts = Post::with('author:name,email')->get();

// CORRECT — id is the primary key the relationship matches against
$posts = Post::with('author:id,name,email')->get();

Eloquent needs the related model's key column (typically id) present in the selected columns to actually associate each loaded author back with the correct post — omitting it is a common mistake that results in every post either failing to show its author or showing incorrect data.

Restricting columns on the base query too

$posts = Post::select('id', 'title', 'author_id')
    ->with('author:id,name')
    ->get();

Similarly, the base query's own select() needs to include author_id (the foreign key on the posts table itself) — without it, Eloquent has no foreign key value on the parent model to match against the eager-loaded related records at all.

Restricting columns on a nested (multi-level) relationship

$posts = Post::with('author:id,name,team_id', 'author.team:id,name')->get();

Each level of a nested eager-load chain needs its own explicit column restriction and its own foreign key included — restricting only the top-level relationship's columns while leaving a nested one unrestricted still fetches every column at that deeper level.

Combining with a closure for more control (like ordering)

$posts = Post::with(['comments' => function ($query) {
    $query->select('id', 'post_id', 'body', 'created_at')
        ->orderBy('created_at', 'desc')
        ->limit(5);
}])->get();

The closure form of with() is necessary once the eager load needs more than just column restriction — ordering, limiting, or additional where conditions on the related query all require this closure syntax rather than the simple colon-separated column list.

Why this optimization matters most on wide tables

The performance benefit of restricting eager-loaded columns scales with how many columns the related table actually has and how many rows are being loaded — for a small, narrow lookup table, the difference is negligible, but for a wide users table (with many profile fields, preferences, and metadata columns) being eager-loaded across a large list of posts, this optimization can measurably reduce both query time and memory usage.