lates

Have you ever needed to show only single related model from the hasMany relationship on a set of parents?

Being it latest, highest or just random, it’s not very clever to load whole collection using eager loading, just like running query per every parent.

Of course you can do that better, and now let me show you how. 

 

Imagine you are building a blog with Categories and Posts. Chances are, the relationship between the two will be simple 1-many:

You want to show categories on the homepage, paginated by 20, and for each category latest post digest. It will look more or less like this:

Now, the question is, how to fetch that single, latest related Post model? Let me show you natural path I would go about it:

  1. Let’s start with single category

    nothing fancy, pretty easy and nothing to worry about here. If you need just a single category, that’s the way you could go. But…
  2. … obviously this won’t scale at all:

    The same query as above executed in the foreach loop = N+1 issue.
  3.  Eager loading to the rescue Fortunately Eloquent provides eager loading in order to avoid this performance hit, so all we need is to eager load the relation:

    1 query for all the related posts instead of 20 queries. That was easy, right? Looks like it’s good, only.. it’s not!

    The thing is, we needed 20 categories + 20 posts, while ,as it happens, we instead loaded bunch of unnecessary posts. Imagine that every category has 100 posts.  So we’ve got 2000 posts, instead of 20. Performance hit might not be huge, but definitely we need to do something with it. We need that ram in the end 😉

  4. So let’s try limiting our eager load query:

    This could come to your mind obviously. Unfortunately this is completely wrong. You see that limit 1? We can’t limit posts to 1 per category this way. All we did is limiting whole query to 1 row.

  5. Getting to the point then! In order to make it work, we’re gonna need a helper relation, that will let us eager load exactly what we wanted:

    Now we can easily do this:

    As you can see, we executed exactly the same query as previously when using eager loading. However this time Eloquent stored only 20 Post objects in memory instead of 2000, because the relation is hasOne.

      We benefited in two ways:

  1. Difference in memory usage can be huge, so we should take advantage of this.
  2. The code is more readable, easier to mantain and use. We replaced chained methods $category->posts->first()->whatever with simple and self-explanatory $category->latestPost;

Stay tuned, next time I’m gonna show you how to do the same with many-to-many relations.

It's only fair to share...Tweet about this on TwitterShare on RedditPin on PinterestShare on FacebookShare on Google+

Related Post

How to store clients in database? That is, Party M... Have you ever been building any app that handled client's data, was it e-commerce, crm or anything similar, you surely wondered, how to store it in ...
Querying relations with Eloquent in Laravel 4 Laravel ORM Eloquent is brilliant! That being said, let's try to tackle very common questions that appear when you try to get related models lo...
Querying relations in Laravel: get Models where la... Another part of Querying relations in Laravel will cover such problem: I want to get SomeModels that have latest RelatedModel (hasMany) matchin...
Laravel – querying any level far relations w... Eloquent provides one Relation type for far related tables - hasManyThrough. However it works only with with cascade of hasOne/hasMany relations a...