This episode is for members only

Sign up to access "Laravel Performance" right now.

Get started
Already a member? Sign in to continue
10. Fetching counts efficiently


So we're heading over to a completely different page
with the starter project now. And this is the author's page. This lists all of the authors that we have in the database, or the users that we have in the database.
And it shows the amount of posts that they have. And we know that's the case because we used our seeder to generate them. But why would this page be slow?
Let's take a look at the debug bar and just see what's going on here. So we've got the query count here, which is just two. We're grabbing all users.
Again, we could limit the select on this, but it's not gonna make too much difference. Then we're selecting all of the posts from the users because we're counting the amount of posts that we have.
We have this eager loaded, which is absolutely fine. So everything looks good. But if you take a look at the memory usage here, you can see this is 167 megabytes
with a 500 millisecond-ish page load time. What is going on here and why is this so slow? Well, let's head over to the controller just here. You can see we're eager loading the posts in,
which we've already looked at. So that's not the issue. The query count is low. Let's head over to the author's index blade file
and take a look at this as well. So we're iterating through the authors. We don't have too many. We have about five and we're outputting the author name.
And from that relationship posts, which is a Laravel collection, we're using the count method. We're actually doing this twice
because we're using the plural functionality of Laravel string helper to output either post if the user has one post or posts if the user has multiple posts.
So the question is, why is this so slow? Well, what we're actually doing here is we have a huge amount of items within the posts collection and we're using that twice here.
What we're then doing is using the count method on a collection directly within PHP. Now this isn't very fast because what this does is it will have to count all of the items
that exist within this posts collection. And that is actually pretty slow. Now, if we get rid of the pluralized version of this, for example, and we just say posts,
if we didn't care about the pluralization, let's take a look at what happens to the memory usage and the request duration. So you can see that it's working
in exactly the same way as well. So it's not that we're doing that twice and that's causing the issue. The whole issue is that we're counting
on this posts collection. Now, this kind of thing is incredibly common where we're showing counts for particular things. So this is a really important tip.
And to solve this, we use database aggregates or counts. So let's go and refactor this to make this better. Now, if we head over to the controller again and we just do a die dump on authors
and we grab the first author from this, this gives us a good indication of what data we're working with here. We have all of this author information,
which we don't necessarily need. So while we're here, what we'll do is just do a select on only the information we need. So we just need the author's ID and the author's name here.
That's gone ahead and reduced the amount of data we get back from here, which is great. Now let's go ahead and again, put in our query method in here
so we can pull these down neatly onto individual lines so we can see what we're doing. And what we're now gonna do is grab the count at the database level.
So if we get rid of this die dump and give this a refresh, you can see that for each of the users that we're grabbing in here, it doesn't really contain any kind of counts.
That is the goal here, to do that at the database level and then just get an integer back that we can display on our template rather than having to do a count within our template.
So to do this, what we do is say with count in here on our query and we choose the relationship name in here, which is really important. So in this case, it's just posts.
So now if we bring back and die dump on the first author, what we actually get here is an additional column. So or an additional attribute on this model, which is posts count.
And of course you can see that is now just an integer value. Now, just a little tip here, if we wanted to find out how this query is constructed, we can actually just say to SQL
and then die dump the result of this. And if we just go down here and die dump on the authors, what we actually get now is a query string here that we can then use to paste into our database client.
If we want to, we can look at exactly what is happening here. So the query here is selecting everything from the post where the user ID equals the user ID from the post table.
And we're grabbing that sub query here as post count. So it's counting that within the same query. Of course, we can also see this in our debug bar, but if you did need to see that in code anywhere,
you can do that. So we're going to go ahead and say, get in here again. We're going to get rid of authors first. And of course, if we refresh the page,
we're going to get the same memory usage and request duration issue, because we are not going ahead and actually utilizing that new attribute that's been added.
So now what we can do is we can just switch this out. So for each of the authors, we now have a post count attribute on there. We can just change this up.
So there's no iterating over or counting anything inside of that Laravel collection. Now, if we refresh the page here, you can see that the memory usage and request duration
hasn't actually gone down. If we just go ahead and get rid of the egoload now, that's really crucial, because now we don't need to egoload in the posts
to be able to grab the count from the collection that's returned from that egoload on that relationship. So if we give this a refresh now, you can see that's down to two megabytes
and about 50 milliseconds. So we've got rid of the need to egoload because we don't need to count on that related model. And we've introduced a with count in here,
which gives us that attribute that we saw earlier. Now, just a couple of tips. You can rename these. So if we were to just go ahead
and die dump on authors and first again, and we give that a refresh, sure enough, we see posts underscore count. That's the default.
Laravel will take the relationship name and it will append on underscore count. What you can do is at the database level, say as, and you can choose any name you want for this.
So you could say author post count if you needed to, for example. And you can see that if we refresh this and look at this, you can see the attribute name has changed it.
Usually I keep these as defaults unless it's going to interfere with anything or unless I have a really good reason to change this around.
But you know that you've got the option to do that now. So now we have a 160 megabyte page down to two megabytes. And we also have the request duration down to about 50 milliseconds, which is perfect.
15 episodes1 hr 9 mins


Let's keep our Laravel applications feeling snappy! In this course, we cover the absolute fundamentals you need to keep in mind when building anything with Laravel.

While Laravel handles a lot for you, it's easy to fall into the trap of not considering and monitoring performance as you go. Keep these tips in your toolbelt, and you'll be able to develop faster apps, from the beginning.

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!


No comments, yet. Be the first to leave a comment.