Database Layer

lib/DbQuery.php provides a lightweight query builder over PDO (works with BbPdo for SQLite/MySQL portability). No ORM, no magic — just sugar over prepared statements.

Basic Usage

$db = new DbQuery($pdo);

// SELECT
$user = $db->table('users')->where('id', 42)->first();
$threads = $db->table('threads')
    ->where('status', 'visible')
    ->orderBy('created_at', 'DESC')
    ->limit(10)
    ->get();

// INSERT
$newId = $db->table('users')->insert([
    'username' => 'alice',
    'email' => 'alice@example.com'
]);

// UPDATE
$db->table('users')->where('id', 42)->update([
    'email' => 'new@example.com'
]);

// DELETE
$db->table('users')->where('status', 'banned')->delete();

Method Reference

Query Building

Method Description
table($table) Set target table (returns clone, immutable)
select(...$cols) Specify columns (default: *)
where($column, $value, $op = '=') Add WHERE clause
whereIn($column, $values) Add WHERE IN clause
whereRaw($sql, $params) Add raw WHERE clause
orderBy($column, $direction = 'ASC') Add ORDER BY
limit($limit) Set LIMIT
offset($offset) Set OFFSET

Execution

Method Returns
get() array (all rows)
first() ?array (single row or null)
count() int
exists() bool
pluck($column) array (single column values)
paginate($perPage, $page = 1) array (items, total, per_page, current_page, last_page)

Write Operations

Method Returns
insert(array $data) int (last insert ID)
insertIgnore(array $data) int (SQLite: INSERT OR IGNORE, MySQL: INSERT IGNORE)
update(array $data) int (affected rows)
delete() int (affected rows)

Raw Queries

Method Returns
raw($sql, $params) array
rawFirst($sql, $params) ?array
rawExec($sql, $params) int (affected rows)

Pagination

$page = $db->table('threads')
    ->where('category_id', 5)
    ->orderBy('created_at', 'DESC')
    ->paginate(15, max(1, (int)($_GET['page'] ?? 1)));

// Returns:
// [
//     'items' => [...],
//     'total' => 42,
//     'per_page' => 15,
//     'current_page' => 1,
//     'last_page' => 3
// ]

Immutability

All query builder methods return a clone of the instance, so you can branch queries:

$baseQuery = $db->table('threads')->where('status', 'visible');

$count = $baseQuery->count();           // doesn't affect $baseQuery
$latest = $baseQuery->orderBy('created_at', 'DESC')->limit(5)->get();

Integration with BbPdo

DbQuery accepts any PDO instance. When used with BbPdo, SQL is automatically normalized for the active driver (SQLite ↔ MySQL):

// In setup.php — BbPdo handles driver differences
$pdo = new BbPdo($dsn, $user, $pass);
$db = new DbQuery($pdo);

// This works on both SQLite and MySQL:
$db->table('roles')->insertIgnore(['name' => 'admin', 'permissions' => '{}']);