How to execute subqueries in Eloquent

Updated: January 16, 2024 By: Guest Contributor Post a comment

Introduction

One of the most powerful features of Laravel’s ORM, Eloquent, is its ability to construct complex queries using an elegant syntax. It helps to keep your database code simple and maintainable. A particularly handy feature is subqueries, which can be a bit tricky to understand and utilize effectively. In this tutorial, we’re going to walk through the process of executing subqueries in Eloquent, from the most basic examples to more advanced concepts. We’ll also explore how coupling Eloquent with Laravel’s query builder can provide even more flexibility and power.

Understanding Eloquent Subqueries

Subqueries are particularly useful when you need to select data from a column that requires a calculation or filtering that is more easily done in a separate query. This might be getting a count, sum, or even running a whole separate transaction within a column’s selection.

Basic Subquery in Select Clause


$userPosts = User::addSelect(['posts_count' => Post::selectRaw('count(*)')
	->whereColumn('user_id', 'users.id')
	->published()])
	->get();

In the code snippet above, User and Post are both Eloquent models. We’re leveraging a basic subquery to add a new select item posts_count which will contain the number of posts each user has. The whereColumn method ensures the count is relevant to each specific user by tying ‘user_id’ from the ‘posts’ table to ‘id’ from the ‘users’ table. The essential idea is to use a subquery as if it’s a column itself.

Advanced Subquery Joins

Joining with subqueries allows you to keep a normalized database while also pre-organizing the data for your application’s logic.

$latestPosts = User::joinSub(
	Post::select('user_id', 'body')
		->whereNotNull('published_at')
		->orderBy('published_at', 'desc'),
	'latest_post',
	function ($join) {
		$join->on('users.id', '=', 'latest_post.user_id');
	}
)
->get();

In the example above, we’re using a subquery to join the users with their latest posts. Notice the use of joinSub which takes three arguments: the subquery, an alias for the subquery, and a closure that defines the join condition. This pattern not only adds versatility to your joins but significantly speeds up your application due to efficient data retrieval.

Subqueries in Where Clauses

Subqueries can also be powerful when used in where clauses. This allows for conditions upon the data that must meet multiple criteria.


$usersWithPopularPosts = User::whereExists(function ($query) {
	$query->select(DB::raw(1))
		->from('posts')
		->whereRaw('posts.user_id = users.id')
		->havingRaw('COUNT(*) > 10');
})
->get();

This query returns users who have more than ten posts. The whereExists method executes the subquery for each user, and if the subquery returns any rows, the user will be included in the final result set.

Combining Subqueries and Aggregate Functions

It often becomes necessary to combine subqueries with aggregate functions to compute a single result from a set of multiple values, such as count, sum, average, etc.


$averagePostScores = User::select(['name'])
->selectSub(
	Post::selectRaw('avg(score) as average_score')
	->whereColumn('user_id', 'users.id'),
	'average_score'
)
->get();

In this example, we use the selectSub method to create a subquery that calculates the average score for each user’s posts and adds it as a {“average_score”} attribute on our resulting User model instances.

Using Raw Expressions in Subqueries

Sometimes, highly specific database operations call for raw expressions within subqueries. Laravel provides the DB::raw and selectRaw methods for these occasions.


$rawSubquery = User::selectRaw('name, (select count(*) from posts where posts.user_id = users.id) as posts_count')
->get();

The above raw subquery within the SELECT statement calculates the number of posts for each user inline. This kind of raw subquery can unlock a variety of highly-tuned and efficient operations but should be used cautiously

Conclusion

By now, you should have a better understanding of how to construct and execute subqueries using Laravel’s Eloquent. Remember, subqueries can be a profoundly effective tool in organizing database logic but utilize them judiciously and always keep an eye on performance metrics.