A typical filtered search — status, category, a date range, all optional — tends to get built as a base query wrapped in several if statements, each conditionally adding a where() call. Eloquent's when() method expresses the same logic inline, as part of the query chain itself.
1. The if-statement version
$query = Product::query();
if ($request->filled('category')) {
$query->where('category_id', $request->category);
}
if ($request->filled('min_price')) {
$query->where('price', '>=', $request->min_price);
}
$products = $query->get();
2. The when() version
$products = Product::query()
->when($request->filled('category'), function ($query) use ($request) {
$query->where('category_id', $request->category);
})
->when($request->filled('min_price'), function ($query) use ($request) {
$query->where('price', '>=', $request->min_price);
})
->get();
when($condition, $callback) runs the callback (with the query builder passed in as its argument) only if $condition is truthy — functionally identical to the if-statement version, just expressed as one continuous fluent chain instead of a base query interrupted by conditional branches.
3. Passing the value directly instead of re-reading $request
$products = Product::query()
->when($request->category, function ($query, $category) {
$query->where('category_id', $category);
})
->get();
When the first argument itself is the value being checked (rather than a separate boolean condition), when() passes that same value as the callback's second argument — this avoids reading $request->category a second time inside the closure, and also means the callback naturally does nothing at all if the value is falsy (null, empty string, 0).
4. An "else" branch with the third argument
$products = Product::query()
->when(
$request->sort === 'price',
fn ($query) => $query->orderBy('price'),
fn ($query) => $query->orderBy('name') // default sort, when the condition is false
)
->get();
5. Using when() for conditional validation rules
$request->validate([
'company_name' => Rule::requiredIf($request->boolean('is_business')),
]);
when() itself is a query-builder method, but the same "apply this rule only if a condition holds" pattern shows up in Laravel's validation layer through Rule::requiredIf() and similar conditional rule helpers — conceptually the same idea (conditional logic expressed declaratively) applied to a different part of the framework.
6. Why this is worth using over plain if statements
Beyond readability, keeping the whole query as one unbroken fluent chain makes it easier to see the query's full shape at a glance, and avoids a subtle bug pattern where a query builder variable gets reassigned incorrectly partway through a long if/elseif chain. For a query with more than two or three optional conditions, when() genuinely scales better in readability than nested or repeated if blocks.