mindy/query_builder
mindy/query_builder offers a fluent, Laravel-style PHP API for building SQL queries programmatically, with composable selects, joins, wheres, and bindings to keep database code readable and safe.
mindy/query_builder is a standalone, framework-agnostic SQL builder for PHP that lets you construct SELECT/INSERT/UPDATE/DELETE statements through a fluent interface and render them as SQL strings bound to PDO parameters. It sits well outside the mainstream: essentially 0 stars, 0 dependents, and a release history that had gone quiet since 2018. Treat it as a small, self-contained utility rather than a community-supported library.
The release of 4.0.4 does change the story slightly — it's evidence of ongoing (if low-profile) maintenance — but it also introduces a breaking change to the query syntax for all four statement types. Before you assume any API shape, read the current source.
For a Laravel developer, the first thing to accept is that Laravel's own DB facade already exposes a query builder with a comparable fluent API (DB::table('users')->where(...)->get()). Where this package earns its place is in non-Laravel PHP contexts — legacy scripts, internal micro-tools, CLI jobs, or small services where you do not want to pull in the full framework just to assemble SQL.
Minimal steps:
composer require mindy/query_builder (pin the version — see Gotchas; note 4.0.4's syntax differs from earlier releases).vendor/mindy/query_builder/src/ — there is no meaningful docs site, and the 4.0.4 release notes are brief and in Russian (Добавлена поддержка json полей, Убрано лишнее экранирование, Изменен синтаксис запросов insert, update, delete, select). You must read the code to learn the current method signatures.\PDO instance (MindyPHP-style frameworks typically set this through a static config or constructor injection — verify in the adapter class).select syntax changed in 4.0.4:// Verify against YOUR source — 4.0.4 changed select/insert/update/delete signatures
$sql = $builder
->select(['id', 'email'])
->from('users')
->where(['status' => 'active'])
->limit(10)
->getSql();
Then execute manually via PDO with the bound params, or via the builder's all()/one() if present. Confirm the execution helpers exist before relying on them — the package still predates many modern conveniences.
Fluent query composition. The primary workflow is method chaining — select → from → join → where → orderBy → limit. Treat the builder as a value object per query; build it inside a repository method and render SQL at the boundary. Because 4.0.4 changed the statement syntax, keep one representative query per statement type in your codebase and re-run it against the new version before trusting the rest.
JSON field support (new in 4.0.4). The release adds support for JSON columns — handy for MySQL JSON columns or Postgres jsonb. Use it for filtering and projecting JSON paths, but keep the same discipline as elsewhere: the JSON path/value arguments should still flow through parameter binding where the API allows it, and complex paths should be treated as trusted identifiers, not user input.
// Confirm the exact method name/signature in your source — this is new API
$builder->from('events')
->where(['payload->type' => 'signup']); // illustrative only — verify
Repository / gateway wrapping. Because the builder has low adoption, isolate it behind your own interface. A UserRepository that internally uses this builder means you can swap to Laravel's builder or Doctrine later without touching call sites — and it gives you a single place to absorb future breaking releases like 4.0.4:
final class UserRepository {
public function __construct(private QueryBuilder $qb) {}
public function active(): array {
return $this->qb->from('users')->where(['status' => 'active'])->all();
}
}
Dialect switching. Mindy-style builders typically ship MySQL/Postgres/SQLite/MSSQL dialects. Resolve the adapter from config at bootstrap and keep query code engine-agnostic — the builder handles identifier quoting and LIMIT/OFFSET differences. Be aware 4.0.4 also reduced escaping to match DBAL behaviour, so the generated SQL is now less aggressively quoted than in earlier releases (see Gotchas).
Binding over interpolation. Always feed values through the where(['col' => $value]) array form or named bindings. Reserve raw expressions (->where(new Expression('...')) or the string overload) for identifiers, NOW(), function calls, and CASE blocks — never for user input.
Composition with pagination and search. Build the base query once, then branch based on request filters. Because the builder returns its own instance, you can clone a base query for count-vs-page:
$count = clone $base; $count->select(['COUNT(*) AS c']);
$page = $base->limit($per)->offset(($pg - 1) * $per);
Integration from Laravel. If you must use it inside a Laravel app (e.g. porting legacy code), wire the PDO connection from DB::connection()->getPdo() and register a small service provider binding. Do not mix this builder's getSql() output with DB::select() expecting Laravel query-log integration — you lose query bindings in the log unless you pass them explicitly.
Изменен синтаксис запросов insert, update, delete, select — the method syntax for all four statement types changed. Any prior code, snippets, or blog examples you find online (including in older versions of this assessment) may no longer compile. Diff your call sites against the current source before upgrading, and check composer why-not mindy/query_builder 4.0.4 if you're already on an earlier 4.x.Убрано лишние экранирование. Экранируются теперь только исключения по аналогии работы dbal. The builder now quotes only when necessary, DBAL-style, instead of blanket-quoting identifiers. Consequences: (a) any tests or assertions that expected backticks around every identifier will start failing; (b) you now own more of the responsibility for quoting identifiers that are reserved words, contain spaces, or are case-sensitive (Postgres). Audit raw identifier construction after upgrading.each(), dynamic properties, or pre-??= patterns that trip PHP 8.2 deprecations. Run php -l over all source files and a smoke test on your target PHP before committing.config/query_builder.php. You must bind it yourself if integrating.whereRaw, having, or Expression path bypasses parameterisation. Combined with 4.0.4's more permissive escaping, treat those as trusted-identifier-only APIs and grep your codebase for them in code review.getSql() + getParams()/getBindings() if present) before executing — especially after a syntax change like 4.0.4's.DB::table(...). If you specifically need a standalone non-Laravel query builder, 4.0.4's JSON support makes it slightly more viable, but cakephp/database or doctrine/dbal's QueryBuilder remain the safer bets — both are maintained, widely used, and won't force a syntax migration on a point release.How can I help you explore Laravel packages today?