-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Muhammet Şafak edited this page May 24, 2026
·
2 revisions
A lightweight, dialect-aware SQL query builder for PHP. Fluent calls in, SQL string + a separate parameter bag out — ready to execute with PDO, without ever concatenating user input into SQL.
composer require initorm/query-builderuse InitORM\QueryBuilder\QueryBuilder;
$qb = new QueryBuilder('mysql');
$qb->select('id', 'name')
->from('users')
->where('status', 1)
->andWhere('country', 'TR')
->orderBy('id', 'DESC')
->limit(20);
echo $qb->generateSelectQuery();
// SELECT `id`, `name` FROM `users`
// WHERE `status` = 1 AND `country` = :country
// ORDER BY `id` DESC LIMIT 20
print_r($qb->getParameter()->all());
// [ ':country' => 'TR' ]Hand the SQL and the bag to PDO:
$stmt = $pdo->prepare($qb->generateSelectQuery());
$stmt->execute($qb->getParameter()->all());
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);That's the entire mental model.
-
Safe by default. Every value flows through a collision-safe parameter
bag (
:col,:col_1,:col_2, …). Raw SQL is opt-in viaRawQuery. - Dialect aware. Identifier escaping is driver-specific: backticks for MySQL / SQLite, double quotes for PostgreSQL, no-op for the generic driver.
- Tiny and predictable. Single namespace, no service container, no reflection, no annotations. About 1 600 lines of code total.
-
Battle-tested clause DSL. Comparison operators, BETWEEN, IN, the LIKE
family (
like/startLike/endLike), NULL checks, REGEXP, SOUNDEX, FIND_IN_SET, parenthesized groups, closure-based JOIN ON expressions, closure-based sub-queries. - 324 tests, 97 % line coverage. Including a dedicated security regression suite covering SQL-injection vectors classic query builders often leave open.
| Chapter | What's in it |
|---|---|
| Getting Started | Install, the first query, the factory, PDO execution. |
| SELECT Queries | Projections, aggregates, string functions, COALESCE, CONCAT, GROUP BY, ORDER BY, LIMIT/OFFSET. |
| WHERE Clauses | Comparison matrix, value-shortcut, NULL, BETWEEN, IN, LIKE family, REGEXP, SOUNDEX, FIND_IN_SET. |
| JOIN Queries |
innerJoin, leftJoin, rightJoin, leftOuterJoin, rightOuterJoin, selfJoin, naturalJoin, closure-based ON. |
| INSERT UPDATE DELETE |
set / addSet, single-row + batch INSERT, single + CASE/WHEN batch UPDATE, DELETE. |
| Sub Queries |
subQuery() in WHERE IN / FROM / JOIN / standalone. |
| Grouped Conditions |
group() for parenthesized WHERE/HAVING/ON. |
| Raw Queries |
RawQuery: when and how to use it responsibly. |
| Parameters | The parameter bag, auto-suffix, NULL short-circuit, PDO handoff. |
| Drivers | MySQL/PgSQL/SQLite/Generic; writing a custom dialect driver. |
| Security | Threat model, defenses, application-level residual risks. |
| Recipes | Pagination, soft-delete, dynamic filters, upsert, ranking, … |
| API Reference | Categorized table of every public method. |
| FAQ | Frequently asked questions. |
| Migration from v1 | Breaking changes between 1.x and 2.x, plus an upgrade walkthrough. |
// SELECT with JOIN, GROUP BY, HAVING
$qb->select('u.id', 'u.name')
->selectCount('p.id', 'post_count')
->from('users', 'u')
->leftJoin('posts AS p', 'p.user_id = u.id')
->where('u.active', 1)
->groupBy('u.id')
->having('post_count', '>', 5)
->orderBy('post_count', 'DESC')
->limit(10);
// INSERT
$qb->from('users')
->set(['name' => 'Muhammet', 'email' => 'info@muhammetsafak.com.tr']);
$qb->generateInsertQuery();
// UPDATE with CASE/WHEN batch
$qb->from('posts')
->set(['id' => 1, 'views' => 100])
->set(['id' => 2, 'views' => 42]);
$qb->generateUpdateBatchQuery('id');
// Sub-query inside WHERE IN
$qb->whereIn('u.id', $qb->subQuery(function ($sub) {
$sub->select('id')->from('roles')->where('name', 'admin');
}));-
In-repo docs —
docs/en/ships the same chapters as Markdown files alongside the source. -
Source code —
src/is small enough to read in an afternoon; if a corner is unclear, the source is the canonical reference. -
Tests —
tests/every wiki example is mirrored by a test that pins the expected SQL.
- Bugs — open an issue.
- Security — follow the org-wide disclosure process. Do not file public issues for security problems.
MIT. © Muhammet ŞAFAK.
InitORM QueryBuilder — MIT licensed · authored by Muhammet ŞAFAK · part of the InitORM family · report an issue · security disclosure