Query Builder

Com fer consultes amb el Query Builder de Laravel: selects, wheres, joins i agregats.

Què és el Query Builder?#

El Query Builder de Laravel permet construir consultes SQL de manera fluida i expressiva sense escriure SQL directament. Genera consultes segures amb prepared statements, protegint contra injeccions SQL. A diferència d'Eloquent (que treballa amb models), el Query Builder opera directament amb les taules de la base de dades.

Consultes bàsiques#

Per començar una consulta, utilitza la facade DB amb el mètode table():

use Illuminate\Support\Facades\DB;
 
// Obtenir tots els registres d'una taula
$users = DB::table('users')->get();
 
// Primer registre que compleix la condició
$user = DB::table('users')->where('email', 'admin@exemple.com')->first();
 
// Valor d'una sola columna
$name = DB::table('users')->where('id', 1)->value('name');
 
// Llista de valors d'una columna
$emails = DB::table('users')->pluck('email');
 
// Llista clau-valor (útil per a selects de formularis)
$users = DB::table('users')->pluck('name', 'id');
// [1 => 'Joan', 2 => 'Maria', 3 => 'Pere']

El mètode get() retorna una col·lecció amb tots els resultats. first() retorna un sol objecte o null si no troba res. value() retorna el valor d'una sola columna del primer resultat.

Si vols assegurar-te que el registre existeix i llançar una excepció 404 si no, utilitza firstOrFail():

$user = DB::table('users')->where('id', $id)->firstOrFail();

Seleccionar columnes#

Per defecte, get() selecciona totes les columnes (SELECT *). Pots limitar-ho amb select():

$users = DB::table('users')
    ->select('name', 'email')
    ->get();
 
// Amb àlies
$users = DB::table('users')
    ->select('name', 'email as correu')
    ->get();
 
// Afegir una columna a la selecció
$users = DB::table('users')
    ->select('name')
    ->addSelect('email')
    ->get();
 
// Valors diferents (DISTINCT)
$cities = DB::table('users')
    ->distinct()
    ->select('city')
    ->get();

Condicions WHERE#

El mètode where() afegeix condicions a la consulta. La forma més bàsica accepta tres paràmetres: columna, operador i valor. Quan l'operador és =, pots ometre'l:

// WHERE active = true
$users = DB::table('users')->where('active', true)->get();
 
// WHERE age >= 18
$users = DB::table('users')->where('age', '>=', 18)->get();
 
// WHERE name LIKE '%Joan%'
$users = DB::table('users')->where('name', 'like', '%Joan%')->get();

Pots encadenar múltiples where() per combinar condicions amb AND:

$users = DB::table('users')
    ->where('active', true)
    ->where('age', '>=', 18)
    ->where('role', 'editor')
    ->get();

Per a condicions OR, utilitza orWhere():

$users = DB::table('users')
    ->where('role', 'admin')
    ->orWhere('role', 'editor')
    ->get();

Condicions avançades#

Laravel proporciona mètodes específics per a les condicions SQL més comunes:

// WHERE IN
$users = DB::table('users')
    ->whereIn('id', [1, 2, 3])
    ->get();
 
// WHERE NOT IN
$users = DB::table('users')
    ->whereNotIn('status', ['banned', 'suspended'])
    ->get();
 
// WHERE BETWEEN
$users = DB::table('users')
    ->whereBetween('age', [18, 65])
    ->get();
 
// WHERE NULL
$unverified = DB::table('users')
    ->whereNull('email_verified_at')
    ->get();
 
// WHERE NOT NULL
$verified = DB::table('users')
    ->whereNotNull('email_verified_at')
    ->get();
 
// WHERE DATE
$todayUsers = DB::table('users')
    ->whereDate('created_at', today())
    ->get();
 
// WHERE columna = altra columna
$users = DB::table('users')
    ->whereColumn('created_at', '=', 'updated_at')
    ->get();

Agrupació de condicions#

Per crear condicions complexes amb parèntesis, passa un closure a where():

// WHERE active = true AND (role = 'admin' OR role = 'editor')
$users = DB::table('users')
    ->where('active', true)
    ->where(function ($query) {
        $query->where('role', 'admin')
              ->orWhere('role', 'editor');
    })
    ->get();

Ordenar i limitar#

// Ordenar per nom ascendent
$users = DB::table('users')
    ->orderBy('name', 'asc')
    ->get();
 
// Ordenar per múltiples columnes
$users = DB::table('users')
    ->orderBy('role', 'asc')
    ->orderBy('name', 'asc')
    ->get();
 
// Últims registres (orderBy created_at DESC)
$latest = DB::table('articles')->latest()->get();
 
// Primers registres (orderBy created_at ASC)
$oldest = DB::table('articles')->oldest()->get();
 
// Ordre aleatori
$random = DB::table('users')->inRandomOrder()->first();
 
// Limitar resultats
$users = DB::table('users')
    ->orderBy('name')
    ->limit(10)
    ->offset(20)
    ->get();

Agregats#

Les funcions d'agregat retornen un valor únic en lloc d'una col·lecció de resultats:

$count = DB::table('users')->count();
$maxAge = DB::table('users')->max('age');
$minPrice = DB::table('products')->min('price');
$avgRating = DB::table('reviews')->avg('rating');
$totalRevenue = DB::table('orders')->where('status', 'paid')->sum('total');
 
// Comprovar si existeixen registres
$exists = DB::table('users')->where('email', $email)->exists();
$doesntExist = DB::table('users')->where('email', $email)->doesntExist();

Joins#

Per combinar dades de múltiples taules, utilitza els mètodes de join:

// INNER JOIN
$articles = DB::table('articles')
    ->join('users', 'articles.user_id', '=', 'users.id')
    ->select('articles.*', 'users.name as author_name')
    ->get();
 
// LEFT JOIN (inclou articles sense autor)
$articles = DB::table('articles')
    ->leftJoin('users', 'articles.user_id', '=', 'users.id')
    ->select('articles.*', 'users.name as author_name')
    ->get();
 
// Join amb múltiples condicions
$articles = DB::table('articles')
    ->join('categories', function ($join) {
        $join->on('articles.category_id', '=', 'categories.id')
             ->where('categories.active', true);
    })
    ->get();

Group By i Having#

Per agrupar resultats i filtrar-los:

// Comptar articles per autor
$authors = DB::table('articles')
    ->join('users', 'articles.user_id', '=', 'users.id')
    ->select('users.name', DB::raw('COUNT(*) as total_articles'))
    ->groupBy('users.name')
    ->having('total_articles', '>', 5)
    ->get();

Inserir, actualitzar i eliminar#

El Query Builder també permet modificar dades:

// Inserir un registre
DB::table('users')->insert([
    'name' => 'Joan',
    'email' => 'joan@exemple.com',
    'created_at' => now(),
]);
 
// Inserir i obtenir l'ID
$id = DB::table('users')->insertGetId([
    'name' => 'Joan',
    'email' => 'joan@exemple.com',
]);
 
// Inserir múltiples registres
DB::table('users')->insert([
    ['name' => 'Joan', 'email' => 'joan@ex.com'],
    ['name' => 'Maria', 'email' => 'maria@ex.com'],
]);
 
// Actualitzar registres
DB::table('users')
    ->where('id', 1)
    ->update(['name' => 'Joan Pere']);
 
// Incrementar o decrementar un valor
DB::table('articles')->where('id', 1)->increment('views');
DB::table('products')->where('id', 1)->decrement('stock', 5);
 
// Actualitzar o inserir (upsert)
DB::table('users')->updateOrInsert(
    ['email' => 'joan@ex.com'],           // Condició de cerca
    ['name' => 'Joan', 'active' => true]    // Valors a actualitzar/inserir
);
 
// Eliminar registres
DB::table('users')->where('active', false)->delete();
 
// Eliminar tots els registres
DB::table('logs')->truncate();

Chunking per a grans volums#

Quan treballes amb milers de registres, carregar-los tots a memòria pot ser un problema. El mètode chunk() processa els resultats en grups:

DB::table('users')->orderBy('id')->chunk(100, function ($users) {
    foreach ($users as $user) {
        // Processar cada usuari
    }
});
 
// Lazy collection (encara més eficient en memòria)
DB::table('users')->orderBy('id')->lazy()->each(function ($user) {
    // Processar cada usuari
});

El chunk() fa una consulta per cada grup de 100 registres, mantenint el consum de memòria baix independentment del nombre total de registres.