Aggregates and Raw Expressions

count(), max(), min(), avg() and sum() return one value (avg('price') gave the string "31.986250"), and exists() a boolean. For SQL the builder has no method for, selectRaw(), whereRaw(), havingRaw(), orderByRaw() and groupByRaw() insert text, and DB::raw() wraps a fragment for any method that accepts one.

Section 3.8.1's monthly revenue, and raw SQL with and without bindingsPHP
DB::table('orders as o')->join('order_items as i', 'i.order_id', '=', 'o.id')
    ->where('o.status', '<>', 'cancelled')
    ->selectRaw("date_format(o.ordered_at, '%Y-%m') as month, count(distinct o.id) as orders")
    ->selectRaw('sum(i.quantity * i.unit_price) as revenue')
    ->groupBy('month')->orderBy('month')->limit(2)
    ->get()->each(fn ($row) => print(json_encode($row)."\n"));
$term = "' or 1=1 or '";
$unsafe = DB::table('products')->whereRaw("title like '%$term%'");
$safe = DB::table('products')->whereRaw('title like ?', ["%$term%"]);
echo $unsafe->toRawSql(), ' -> ', $unsafe->count(), " rows\n";
echo $safe->toSql(), ' -> ', $safe->count(), " rows\n";
Output
{"month":"2026-03","orders":2,"revenue":"153.37"}
{"month":"2026-04","orders":1,"revenue":"49.00"}
select * from `products` where title like '%' or 1=1 or '%' -> 8 rows
select * from `products` where title like ? -> 0 rows

The months match MySQL. The last two lines are How SQL Injection Works in miniature: interpolated, the term's quote closed the literal and or 1=1 matched everything; bound, it was data and matched nothing. For every *Raw() method, the SQL text is yours and values go in the bindings array; Laravel 2,157 's documentation says it cannot guarantee raw expressions are safe. Column names cannot be bound at all, so map a ?sort= value through an allow-list array.