Eager Loading Selected Model Fields
Eager loading is an essential aspect of any Laravel application. Eager loading allows you to tell Laravel to fetch lots of database records ahead of time, saving your application from having to loop over records and perform database connections for each.
Sometimes, you don't need the whole model however. Maybe you have memory overhead issues. Maybe you just want to keep your code as efficient as possible. In any case here's how you can use Laravel to eager load selected columns on a model.
Using The Colon Operator
You can select the columns you want to select from an eager loaded model relation by using a :
followed by the comma separated fields you want to select:
$posts = Post::query()
->with(['image:id,disk_name'])
->get();
The above statement will get all your Post
models, and eager load all of the Image
models for each of those posts. When performing the select however, Laravel will only fill the id
and disk_name
fields of the model.
Using The Query Builder
$posts = Post::query()
->with(['image' => function (Builder $query) {
return $query->select('id', 'disk_name');
}])
->get();
Above is the same exact example, but using the slightly more wordy query builder method. This can be useful if you need to perform other logic on the query.
The Full Example
We'll start with the classic Laravel model tutorial example: The Blog. (shocking I know 🤯).
Imagine you have a database structure where a blog post has a single image relation that contains the path to the image, and metadata about that image.
Now if we have say 300 blog posts, that's potentially A LOT of metadata we're going to load. And it might not always be the case that you need it. What we can do in those situations is only pull the columns we need from the database. The result of this will be a reduced memory overhead and a potential speed increase - Laravel won't have to fill hundreds of models with data that might not be used.
In this example your model might look like this:
class Post extends Model
{
public function image(): HasOne
{
return $this->hasOne(Image::class);
}
}
And when we query the models, instead of this...
Post::query()->with('image')->get();
We use this...
$posts = Post::query()
->with('image:id,disk_name')
->get();
Simple.
If you need to perform additional checks on the eager load query you can also include this select statement in the longform version in a builder statement:
$posts = Post::query()
->with('image' => function (Builder $query) {
return $query
->whereTrue('public')
->select('id', 'disk_name');
})
->get();
Conclusion
Its a simple technique, but important to remember. It's very easy to let N+1 and memory issues slip in an application.
Hope this helps 🤙
No comments yet…