Salt la conținut

Query Builder

Query builder-ul db() oferă acces direct la baza de date fără a trece prin modele ORM. Este util pentru interogări complexe, agregări, DDL și operațiuni care nu se potrivesc pe un singur model.

ORM sau Query Builder? Pentru citiri și scrieri pe o entitate (cu hook-uri, ACL, relații) folosește modelul: User::where(...)->get(). Pentru agregări, JOIN-uri pe mai multe tabele, DDL sau SQL brut, folosește db(). Cele două pot fi combinate liber.

Funcția db()

// Conexiunea implicită (din config/database.php)
$conn = db();

// Conexiune numită
$conn = db('company');
$conn = db('workleto');

// Dotted notation: instance + database override
$conn = db('workleto.company_data');

Funcția returnează o instanță Connection care expune builder-e fluente pentru fiecare tip de operațiune.


SELECT

// Select simplu
$users = db('company')
    ->select('id', 'name', 'email')
    ->from('users')
    ->where('active', 1)
    ->orderBy('name')
    ->limit(10)
    ->resultset();

// Select cu *
$all = db()->select('*')->from('modules')->resultset();

// O singură coloană
$names = db('company')
    ->select('name')
    ->from('users')
    ->col();

// Un singur rând
$user = db('company')
    ->select('*')
    ->from('users')
    ->where('id', 1)
    ->single();

// O singură celulă (prima celulă a primului rând)
$name = db('company')
    ->select('name')
    ->from('users')
    ->where('id', 1)
    ->cell();

Numărare și existență

Pentru numărat sau verificat existența, folosește helper-ele dedicate. Nu scrie COUNT(*) manual:

// Câte rânduri se potrivesc
$total = db('company')->select()->from('users')->where('active', 1)->count();

// Există măcar un rând? (rulează SELECT 1 ... LIMIT 1)
$hasAdmin = db('company')->select()->from('users')->where('role', 'admin')->exists();

// Valori distincte
$roles = db('company')->select('role')->from('users')->distinct()->col();

WHERE

db('company')->select()->from('users')
    ->where('status', 'active')               // egal
    ->where('age', '>', 18)                   // operator explicit
    ->where('name', 'LIKE', '%Ion%')          // LIKE
    ->where('id', 'IN', [1, 2, 3])            // IN
    ->where('deleted_at', null)               // IS NULL
    ->orWhere('role', 'admin')                // OR
    ->whereGroup(function ($q) {              // grup cu paranteze
        $q->where('a', 1)->orWhere('b', 2);
    })
    ->resultset();

JOIN, GROUP BY, HAVING

use Leto\Database\QueryBuilder\Enums\JoinType;

db('company')
    ->select('u.name', 'COUNT(o.id) as order_count')
    ->from('users u')
    ->join(JoinType::LEFT, 'orders o', 'u.id = o.user_id')
    ->groupBy('u.id')
    ->having('order_count', '>', 5)
    ->orderBy('order_count', 'DESC')
    ->resultset();
La nivelul db(), orderBy() primește direcția ca string ('ASC' / 'DESC'). Enum-ul Order aparține stratului ORM (Model::...->orderBy(...)), nu query builder-ului: dacă îl pasezi aici, primești un TypeError.

Paginare

$results = db('company')
    ->select('*')
    ->from('users')
    ->orderBy('name')
    ->page(1, 20)      // pagina 1, câte 20
    ->resultset();

INSERT

// Inserare simplă
db('company')->insert('users', [
    'name' => 'Ion Popescu',
    'email' => 'ion@example.com',
])->execute();

// Obține ID-ul inserat
$id = db('company')->insert_id();

Exemplu real:

db()->insert('companies', [
    'name' => $nume,
    'namespace' => $namespace,
    'package' => $package,
])->execute();

UPDATE

db('company')
    ->update('users')
    ->set('name', 'Nume nou')
    ->set('email', 'email@nou.com')
    ->where('id', 1)
    ->execute();

Exemplu real:

db('company')->update('company_api_tokens', [
    'last_used' => date('Y-m-d H:i:s'),
    'count' => $token['count'] + 1,
])
->where('token_uuid', $tokenUuid)
->execute();

DELETE

db('company')
    ->delete('users')
    ->where('id', 1)
    ->execute();

Exemplu real:

db()->delete('modules')
    ->where('module_name', $moduleName)
    ->execute();

Tranzacții

db('company')->withTransaction(function () {
    db('company')->insert('orders', ['total' => 100])->execute();
    $orderId = db('company')->insert_id();

    db('company')->insert('order_items', [
        'order_id' => $orderId,
        'product_id' => 5,
        'quantity' => 2,
    ])->execute();
});

Metoda withTransaction() execută closure-ul într-o tranzacție: face commit automat la final sau rollback dacă apare o excepție.


DDL

// CREATE TABLE
db()->createTable('logs')
    ->column('id', 'INT')
    ->column('message', 'VARCHAR(500)')
    ->column('context', 'TEXT')
    ->column('created_at', 'TIMESTAMP')
    ->autoIncrement('id')
    ->execute();

// ALTER TABLE
db()->alter('users')
    ->addColumn('phone', 'VARCHAR(20)')
    ->execute();

// DROP TABLE
db()->dropTable('temp_data')->execute();

// TRUNCATE
db()->truncate('sessions')->execute();

Enum-uri

JoinType — tipuri de JOIN

namespace Leto\Database\QueryBuilder\Enums;

enum JoinType
{
    case INNER;
    case LEFT;
    case RIGHT;
}

Folosit cu metoda join() în loc de shortcut-urile leftJoin()/innerJoin()/rightJoin():

use Leto\Database\QueryBuilder\Enums\JoinType;

db('company')
    ->select('u.*', 'o.total')
    ->from('users u')
    ->join(JoinType::INNER, 'orders o', 'u.id = o.user_id')
    ->resultset();

SQL brut

Când o interogare nu se poate exprima prin builder-ul fluent (funcții de fereastră, CTE, sintaxă specifică), folosește query() cu parametri legați (bound):

$rows = db('company')
    ->query('SELECT * FROM users WHERE created_at > :from AND role = :role', [
        'from' => '2026-01-01',
        'role' => 'admin',
    ])
    ->resultset();

Valorile din al doilea argument sunt întotdeauna legate ca parametri (protejate împotriva SQL injection). query() returnează un statement pe care poți apela resultset(), single(), cell() sau col(), la fel ca la builder.


cell(), single(), col(), resultset()

MetodăReturnează
resultset()array de rânduri (array-uri asociative)
single()Un singur rând (array asociativ) sau false dacă nu găsește nimic
cell()O singură valoare: prima celulă a primului rând (sau coloana cu numele dat: cell('nume'))
col()Un array cu valorile unei singure coloane

Conexiuni multiple

// Conectare la baza de date a unei companii specifice
$db = db('workleto.' . DB_PREFIX_COMPANY_DATA . $companyNamespace);
$data = $db->select('*')->from('users')->resultset();

Dotted notation-ul workleto.nume_db refolosește configurația instanței workleto dar schimbă baza de date.