Laravel
Subquery
Where In
PHP
Database Query

How to do this in Laravel, subquery where in

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

In Laravel, a whereIn subquery is useful when the outer query should keep rows whose key appears in the result of another query. The ORM and query builder both support this cleanly, but the main thing is to choose the SQL shape that matches the problem. Sometimes whereIn is correct, and sometimes exists or a join is clearer and faster.

Basic whereIn Subquery with the Query Builder

Suppose you want all users who have at least one published post. In SQL, that can be written as a WHERE IN against the set of author IDs from the posts table. Laravel can express that directly.

php
1use Illuminate\Support\Facades\DB;
2
3$users = DB::table('users')
4    ->whereIn('id', function ($query) {
5        $query->select('author_id')
6            ->from('posts')
7            ->where('status', 'published');
8    })
9    ->get();

Laravel turns that into a subquery inside the IN clause. This pattern works well when the inner query naturally returns a single column of candidate IDs.

Eloquent Version

The same idea works with Eloquent models. If you already use models for the outer query, the subquery can still be built inline.

php
1use App\Models\User;
2use Illuminate\Support\Facades\DB;
3
4$users = User::query()
5    ->whereIn('id', function ($query) {
6        $query->select('author_id')
7            ->from('posts')
8            ->where('status', 'published');
9    })
10    ->get();

This is useful when the result should still be a collection of User models rather than generic database rows.

Use a Query Object as the Subquery

If the inner query is reused or large, build it separately so the code is easier to read and test.

php
1use App\Models\Post;
2use App\Models\User;
3
4$publishedAuthorIds = Post::query()
5    ->select('author_id')
6    ->where('status', 'published');
7
8$users = User::query()
9    ->whereIn('id', $publishedAuthorIds)
10    ->get();

Passing a query object directly is often cleaner than nesting a long closure, especially when extra filters are added later.

When exists Is Better

whereIn is not always the best choice. If the goal is "keep outer rows when a related row exists," a correlated exists query is often a better semantic match.

php
1use App\Models\User;
2use Illuminate\Support\Facades\DB;
3
4$users = User::query()
5    ->whereExists(function ($query) {
6        $query->select(DB::raw(1))
7            ->from('posts')
8            ->whereColumn('posts.author_id', 'users.id')
9            ->where('posts.status', 'published');
10    })
11    ->get();

The database can often optimize exists well because it only needs to know whether at least one matching row exists, not gather the whole inner set first.

Mind the Returned Column Count

A whereIn subquery must return exactly one column. This is a common mistake when the subquery grows.

Correct:

php
$query->select('author_id')->from('posts');

Incorrect:

php
$query->select('author_id', 'title')->from('posts');

If the subquery returns two columns, the generated SQL is invalid for IN.

Keep the Types Compatible

The outer column and inner selected column should represent the same kind of value. If the outer clause is users.id, the inner subquery should usually return user IDs or author IDs with the same underlying type. Mismatched types can lead to wrong results or poor database plans.

This matters especially when one side is stored as a string and the other as an integer, or when a UUID column is compared to numeric IDs by mistake.

Prefer Relationships When the Model Already Has Them

If the real question is "users who have published posts," an Eloquent relationship query may be clearer than a manual subquery.

php
1use App\Models\User;
2
3$users = User::query()
4    ->whereHas('posts', function ($query) {
5        $query->where('status', 'published');
6    })
7    ->get();

This still becomes efficient SQL, but it expresses the domain rule at the relationship level rather than at the raw key-matching level.

Common Pitfalls

  • Returning more than one column from the subquery used by whereIn.
  • Using whereIn when a correlated exists query or whereHas would better express the intent.
  • Comparing columns with incompatible types.
  • Writing a large nested closure when a separate reusable query object would be clearer.
  • Forgetting that IN over a very large inner result set may perform differently from exists depending on the database and indexes.

Summary

  • Use whereIn when the outer query should match a set of IDs returned by another query.
  • Laravel supports this with either an inline closure or a reusable query object.
  • Make sure the subquery returns exactly one column.
  • Consider exists or whereHas when the real intent is relationship existence rather than list membership.
  • Match data types and keep indexes in mind for production queries.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.