If you have worked with Laravel and MySQL for a while, you have probably run into this situation:
User::where('name', 'john')->get();
You expect this to find only:
john
But depending on your MySQL collation, it may also match:
John
JOHN
JoHn
This happens because many MySQL string comparisons are case-insensitive by default.
Laravel 13.x now has a small but useful addition to the Query Builder:
whereBinary()
It gives us a clean way to tell the database:
"Compare this value exactly, using a binary comparison."
The feature was added in Laravel framework PR #61261. It also adds whereNotBinary(), orWhereBinary(), and orWhereNotBinary().
Let's see how it works.
The problem: normal string comparisons.
Imagine we have a users table:
id | name
---|------
1 | John
2 | john
3 | JOHN
4 | Alice
Now we run:
$users = DB::table('users')
->where('name', 'john')
->get();
Depending on the column's collation, MySQL may consider these values equal:
John
john
JOHN
So our query can return more records than we expected.
This isn't a Laravel problem, it's how MySQL string comparison works with a case-insensitive collation.
The old Laravel solution
Before whereBinary(), you could write a raw expression:
$users = DB::table('users')
->whereRaw('name = BINARY ?', ['john'])
->get();
This works, but it isn't particularly nice.
You're mixing Laravel's query builder with database-specific SQL.
You might also have seen code like this:
DB::table('users')
->where('name', '=', DB::raw('BINARY "john"'))
->get();
Again, it works, but it's not very expressive.
Laravel's new method makes the intention much clearer.
Meet whereBinary()
Now we can simply write:
$users = DB::table('users')
->whereBinary('name', 'john')
->get();
That's much easier to read.
The meaning is:
Find users where
nameis exactlyjohn, using a binary comparison.
For MySQL, Laravel generates SQL equivalent to:
select *
from `users`
where `name` = binary ?
with john passed as a bound parameter.
This is exactly what the new Laravel tests verify.
What does "binary" actually mean?
This is the important part.
A normal comparison might treat:
john
John
JOHN
as equal.
A binary comparison cares about the actual characters.
So:
john != John
john != JOHN
John != JOHN
But:
john == john
For example:
DB::table('users')
->whereBinary('name', 'john')
->get();
could match:
john
but not:
John
JOHN
JoHn
This is particularly useful when the exact casing matters.
whereNotBinary()
The PR also adds the opposite operation:
whereNotBinary()
For example:
$users = DB::table('users')
->whereNotBinary('name', 'john')
->get();
This means:
Find rows where
nameis not exactlyjohn.
Laravel generates:
where `name` != binary ?
So if the database contains:
john
John
JOHN
Alice
whereNotBinary('name', 'john') can match:
John
JOHN
Alice
but not:
john
orWhereBinary()
You can also use the binary comparison with OR.
For example:
$users = DB::table('users')
->where('id', 1)
->orWhereBinary('name', 'john')
->get();
This produces the equivalent of:
select *
from `users`
where `id` = ?
or `name` = binary ?
This is useful when you want to combine a normal condition with a case-sensitive condition.
orWhereNotBinary()
And, of course, there is also:
orWhereNotBinary()
For example:
$users = DB::table('users')
->where('id', 1)
->orWhereNotBinary('name', 'john')
->get();
The generated condition is equivalent to:
where `id` = ?
or `name` != binary ?
So we now have four methods:
| Method | Meaning |
|---|---|
whereBinary() |
Exact binary equality |
whereNotBinary() |
Not exactly equal |
orWhereBinary() |
Binary equality using OR
|
orWhereNotBinary() |
Binary inequality using OR
|
An example with usernames
This is probably one of the easiest real-world examples.
Imagine your application has usernames:
john
John
john123
John123
If your application treats usernames as case-sensitive, you might want:
$user = DB::table('users')
->whereBinary('username', 'John')
->first();
Now you're explicitly asking the database for the exact value:
John
rather than relying on whatever collation happens to be configured on the column.
An example with API keys
Another possible use case is comparing values where the exact string matters.
For example:
$key = DB::table('api_keys')
->whereBinary('key', $providedKey)
->first();
The comparison is binary rather than a normal case-insensitive string comparison.
However, don't confuse this with password hashing or password security.
You should still store passwords using a proper password hashing algorithm such as Laravel's password hashing facilities.
whereBinary() is about database string comparison, not password security.
It also works with Eloquent
Because Eloquent uses Laravel's query builder underneath, you can use the method in an Eloquent query as well:
$user = User::query()
->whereBinary('name', 'john')
->first();
And:
$users = User::query()
->whereNotBinary('name', 'john')
->get();
This makes it convenient when you're already working with models.
What happens behind the scenes?
The implementation is actually quite small.
Laravel adds a new Binary where type to the query builder.
Conceptually, this:
->whereBinary('name', 'john')
gets stored as a binary where condition.
The MySQL grammar then turns it into:
`name` = binary ?
For whereNotBinary() it becomes:
`name` != binary ?
The value is still passed as a normal query binding, so you don't need to manually put the value into a raw SQL string.
This is one of the nice things about the new API: you get the database-specific comparison without having to write the raw SQL yourself.
What about PostgreSQL and SQLite?
This is an important detail.
This new feature is not pretending that every database supports the same binary comparison syntax.
The base Laravel grammar throws an exception if the database grammar doesn't support binary comparisons.
The tests in the PR explicitly verify this behavior for PostgreSQL, SQLite, and SQL Server.
MySQL and MariaDB, on the other hand, compile the condition using:
= binary
or:
!= binary
So if your application uses MySQL or MariaDB, this feature is directly relevant.
If your application supports multiple database engines, keep this database-specific behavior in mind.
When should you use whereBinary()?
A good rule is:
Use it when the exact characters matter.
For example:
->whereBinary('username', $username)
when usernames are case-sensitive.
Or:
->whereBinary('code', $code)
when a code should match exactly.
Or:
->whereBinary('slug', $slug)
if your application intentionally treats slug casing as significant.
When should you NOT use it?
Don't automatically replace every where() with whereBinary().
For example, if you want a normal user search:
User::where('name', 'john')->get();
you may actually want:
John
john
JOHN
to be considered the same.
In that situation, a case-insensitive comparison is useful.
The important thing is to decide what your application means by "equal".
One small API, one useful problem solved
The new API isn't a huge change.
That's actually what makes it nice.
Instead of writing:
->whereRaw('name = BINARY ?', [$name])
you can now write:
->whereBinary('name', $name)
And instead of:
->whereRaw('name != BINARY ?', [$name])
you can write:
->whereNotBinary('name', $name)
Your intent becomes much easier to understand when reading the code.
The complete example
Here's a simple example showing all four methods:
// Exact binary match
User::whereBinary('name', 'john')->get();
// Exact binary non-match
User::whereNotBinary('name', 'john')->get();
// Binary match with OR
User::where('active', true)
->orWhereBinary('name', 'john')
->get();
// Binary non-match with OR
User::where('active', true)
->orWhereNotBinary('name', 'john')
->get();
Summay
whereBinary() is a small addition, but it solves a common source of confusion when working with MySQL and case-sensitive values.
The biggest benefit isn't that it makes something possible that you couldn't do before. You could already use BINARY through raw SQL.
The benefit is that Laravel now gives us a clear query-builder API for saying:
whereBinary('name', 'john')
instead of having to drop down into:
whereRaw('name = BINARY ?', ['john'])
If you're using MySQL or MariaDB and you need an exact, case-sensitive string comparison, whereBinary() is a much cleaner way to express that intent.
Top comments (0)