Laravel
Eloquent
OrderBy
Relationship
QueryBuilder

Laravel OrderBy relationship count

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Laravel Eloquent, the normal way to order models by the number of related records is withCount() followed by orderBy() or orderByDesc(). The reason this works is that withCount adds a computed {relationship}_count column to the query, which you can then sort like any other selected column.

Basic pattern with withCount

Suppose a User has many Post records:

php
1class User extends Model
2{
3    public function posts()
4    {
5        return $this->hasMany(Post::class);
6    }
7}

Then:

php
$users = User::withCount('posts')
    ->orderByDesc('posts_count')
    ->get();

Now each User model includes a posts_count attribute, and the result is ordered by that count.

This is the standard answer. You do not need to manually join and group in the common case.

Why the column is named posts_count

Laravel follows a simple naming rule:

text
relationship_name + _count

So:

  • 'posts() becomes posts_count'
  • 'comments() becomes comments_count'

That means the order column does not exist until withCount() is part of the query.

Conditional relationship counts

You can count only a filtered subset of related records:

php
1$users = User::withCount([
2    'posts as published_posts_count' => function ($query) {
3        $query->where('published', true);
4    }
5])
6->orderByDesc('published_posts_count')
7->get();

This is useful when the real requirement is not "most posts overall" but "most published posts" or "most posts in the last 30 days."

Aliases keep the query readable when several counts are involved.

Multiple counts in one query

Laravel can add several relationship counts at once:

php
1$users = User::withCount([
2    'posts',
3    'comments',
4    'likes',
5])->orderByDesc('posts_count')->get();

That gives each model:

  • 'posts_count'
  • 'comments_count'
  • 'likes_count'

You can sort by any of them.

Sorting ascending or combining with eager loading

Descending order is common when you want the most active models first, but ascending order works the same way:

php
$users = User::withCount('posts')
    ->orderBy('posts_count')
    ->get();

If the page also needs the related posts themselves, combine withCount with eager loading:

php
1$users = User::with('posts')
2    ->withCount('posts')
3    ->orderByDesc('posts_count')
4    ->get();

That keeps the count and the related data available in the same result set.

Filtering after withCount

Once the computed count column exists, you may want to filter on it too. Because it is part of the selected result, not a real table column, queries often use having when needed:

php
1$users = User::withCount('posts')
2    ->having('posts_count', '>', 5)
3    ->orderByDesc('posts_count')
4    ->get();

This is a common extension when you want "users with more than five posts, ordered by post count."

When not to use withCount

withCount is great for normal queries, but if the dataset is very large and the count is needed constantly, you may eventually consider a denormalized counter column such as users.posts_count.

That is not because withCount is wrong. It is because at scale, repeated count subqueries may become expensive compared with maintaining a cached counter.

For most ordinary application queries, though, withCount is the clean and idiomatic Eloquent solution.

Common Pitfalls

The biggest mistake is trying to order by posts_count without calling withCount('posts') first. The generated column does not exist otherwise.

Another mistake is using the wrong count column name. The generated attribute follows the relationship method name, not whatever table name you had in mind.

Developers also forget that withCount adds only the count, not the actual related models. If you also need the relationship data itself, add with('posts') separately.

Finally, do not overcomplicate the query with manual joins unless you actually need join-level control. Eloquent already has the simple count-ordering pattern built in.

Summary

  • Use withCount('relationship') to add a computed relationship count column.
  • Order by that generated column with orderBy or orderByDesc.
  • Use aliases and closures for filtered counts such as published-only records.
  • Use having when filtering on the computed count result.
  • For normal Laravel code, withCount is the idiomatic way to sort by relationship count.

Course illustration
Course illustration

All Rights Reserved.