Laravel Magazine
whereAny() and whereAll(): Searching Multiple Columns Without Repeating orWhere

whereAny() and whereAll(): Searching Multiple Columns Without Repeating orWhere

Eric Van Johnson ·

You've written this query a hundred times: a search box that needs to match against a name, an email, and maybe a company field, all with the same operator.

The old way

$users = User::where(function ($query) use ($term) {
    $query->where('name', 'like', "%{$term}%")
        ->orWhere('email', 'like', "%{$term}%")
        ->orWhere('company', 'like', "%{$term}%");
})->get();

It works, but it's boilerplate you have to get right every time — forget the wrapping closure and you'll accidentally AND your search term with every other constraint on the query instead of scoping the OR correctly.

whereAny()

$users = User::whereAny(
    ['name', 'email', 'company'],
    'like',
    "%{$term}%"
)->get();

Same result, one line. whereAny() takes an array of columns, an operator, and a value, and applies the condition across all of them joined with OR, automatically wrapped in its own group — so it composes safely with other where() calls on the same query.

$users = User::where('is_active', true)
    ->whereAny(['name', 'email', 'company'], 'like', "%{$term}%")
    ->get();

That gives you WHERE is_active = 1 AND (name LIKE ? OR email LIKE ? OR company LIKE ?) — exactly the grouping you'd want, without writing the closure yourself.

whereAll()

whereAll() is the AND equivalent — useful when a value needs to match across every column, which comes up more than you'd think with denormalized or mirrored fields:

$products = Product::whereAll(
    ['primary_locale', 'fallback_locale'],
    '=',
    'en'
)->get();

Or checking that several boolean flags are all true without spelling out each one:

$posts = Post::whereAll(
    ['is_published', 'is_reviewed', 'is_indexed'],
    true
)->get();

Mixing with whereNot

Both have negated counterparts, whereNone(), that flip the logic:

// No column contains "test"
$users = User::whereNone(['name', 'email'], 'like', '%test%')->get();

When to reach for these vs. a full-text index

whereAny() is sugar over LIKE queries under the hood — it's not doing anything a database index can optimize the way a proper full-text search would. For an admin search box scanning a few thousand rows, it's perfectly fine and a lot more readable than the closure version. For public-facing search across a large table, you still want Scout, a full-text index, or a dedicated search engine — whereAny() just makes the small, everyday version of this pattern less annoying to write.

Stay Updated

Subscribe to our newsletter

Get latest news, tutorials, community articles and podcast episodes delivered to your inbox.

Weekly articles
We send a new issue of the newsletter every week on Friday.
No spam
We'll never share your email address and you can opt out at any time.