Laravel Tutorial · Database and Eloquent · Lesson 14 of 27
The Query Builder
Query the database fluently with DB::table(): where clauses, ordering, aggregates, joins and grouping, with bindings that prevent SQL injection.
Fluent queries
DB::table('orders') starts a query; chain methods and finish with get(), first(), value(), pluck(), count(), sum(), exists(), paginate()… Values are always bound as parameters, so user input can't inject SQL.
| Need | Method |
|---|---|
| Filter | where('status', 'paid'), where('total', '>', 100), orWhere, whereIn, whereNull, whereBetween, whereDate |
| Sort / limit | orderBy('created_at', 'desc'), latest(), limit(10), offset(20) |
| Aggregates | count(), sum('total'), avg(), max() |
| Joins / groups | join('users', 'users.id', '=', 'orders.user_id'), groupBy, having, selectRaw |
| Write | insert, update, increment, delete, upsert |
Use toSql() (or toRawSql()) to see the SQL a builder will run.
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
Schema::create('orders', function (Blueprint $t) {
$t->id();
$t->string('customer');
$t->string('status');
$t->integer('total_cents');
});
DB::table('orders')->insert([
['customer' => 'ana', 'status' => 'paid', 'total_cents' => 4500],
['customer' => 'ben', 'status' => 'pending', 'total_cents' => 1200],
['customer' => 'ana', 'status' => 'paid', 'total_cents' => 9900],
['customer' => 'cleo', 'status' => 'refunded', 'total_cents' => 3000],
['customer' => 'ben', 'status' => 'paid', 'total_cents' => 2500],
]);
$big = DB::table('orders')->where('status', 'paid')->where('total_cents', '>', 3000)->orderByDesc('total_cents')->pluck('customer');
echo 'big paid orders by: ', $big->implode(', '), "\n";
echo 'paid revenue: ', DB::table('orders')->where('status', 'paid')->sum('total_cents') / 100, "\n";
echo 'customers: ', DB::table('orders')->distinct()->orderBy('customer')->pluck('customer')->implode(', '), "\n";
$perCustomer = DB::table('orders')
->select('customer', DB::raw('count(*) as orders'), DB::raw('sum(total_cents) as cents'))
->whereIn('status', ['paid', 'pending'])
->groupBy('customer')
->having(DB::raw('sum(total_cents)'), '>', 2000)
->orderBy('customer')
->get();
foreach ($perCustomer as $row) {
echo "$row->customer: $row->orders order(s), ", $row->cents / 100, "\n";
}
DB::table('orders')->where('status', 'pending')->update(['status' => 'paid']);
echo 'pending left: ', DB::table('orders')->where('status', 'pending')->count(), "\n";
$evil = "ana' OR '1'='1";
echo 'injection attempt matches: ', DB::table('orders')->where('customer', $evil)->count(), "\n";
echo DB::table('orders')->where('customer', 'ana')->orderBy('id')->toSql(), "\n";
Output
big paid orders by: ana, ana paid revenue: 169 customers: ana, ben, cleo ana: 2 order(s), 144 ben: 2 order(s), 37 pending left: 0 injection attempt matches: 0 select * from "orders" where "customer" = ? order by "id" asc
Run this on your computer — see “Get Started” for setup.
Test yourself
How does the query builder protect against SQL injection?
It escapes HTMLValues are sent as bound parameters, separate from the SQL textIt blocks quotesIt only allows numbers
Show answer
Answer: Values are sent as bound parameters, separate from the SQL text