Reader Stacks

Writing Raw SQL Queries in Laravel Safely

When a raw query is actually justified over Eloquent, and why parameter binding — not string concatenation — is the difference between a raw query and a SQL injection bug.

Writing Raw SQL Queries in Laravel Safely

Eloquent and the query builder cover the large majority of real application queries — reach for a raw query only when you hit something they genuinely can't express cleanly: a complex window function, a database-specific feature, or a query where the generated SQL from the builder is measurably worse than hand-written SQL.

The non-negotiable rule: always use bindings

// Safe — value is bound, not concatenated
$users = DB::select('select * from users where status = ?', [$status]);

// Never do this — direct string interpolation is a SQL injection bug
$users = DB::select("select * from users where status = '$status'");

The ? placeholder (or named :status bindings) tells the database driver to treat the value strictly as data, never as part of the query structure — this is the actual mechanism that prevents injection, not "escaping" the string yourself.

Named bindings for readability

DB::select('select * from orders where user_id = :user and status = :status', [
    'user' => $userId,
    'status' => 'pending',
]);

Raw expressions inside the query builder

You don't have to drop to a fully raw query just to use one raw expression — DB::raw() (or the selectRaw()/whereRaw()/orderByRaw() helpers) let you mix a raw fragment into an otherwise normal builder query:

User::selectRaw('count(*) as total, status')
    ->groupBy('status')
    ->get();

This is usually the better middle ground — you keep Eloquent's model hydration and query composition, while writing raw SQL only for the specific piece that needs it.

Inserts, updates, and deletes

DB::insert('insert into logs (message) values (?)', [$message]);
DB::update('update users set active = ? where id = ?', [true, $id]);
DB::delete('delete from sessions where last_activity < ?', [$cutoff]);

When NOT to reach for raw SQL

If the only reason for a raw query is that the Eloquent version "felt slower to write," that's not a good enough reason — raw queries lose model events, casts, and relationship eager loading, and they're harder for the next person to read. Reserve them for cases the query builder genuinely can't express.

Topics: Database Queries & Eloquent