feat(db): expand e_db_query to Laravel and Doctrine DBAL parity#5763
Merged
Conversation
Add the query-builder surface a developer coming from Laravel or Doctrine DBAL would expect, on top of the existing safe core (values are always bound; identifiers are validated and fail closed): - WHERE: value forms where(col, val) / where(col, op, val), column => value and tuple arrays, and closures for parenthesised groups. Chained where() appends with AND and orWhere() with OR (no implicit reset); andWhere() stays as an alias. Plus whereNull/whereNotNull, whereIn/whereNotIn (array or sub-query), whereBetween/whereNotBetween, whereColumn, whereLike/ whereNotLike, whereExists/whereNotExists, whereNot, and date, JSON and full-text helpers, each with an orWhere* sibling. - HAVING: value forms, orHaving, havingRaw, havingBetween. - SELECT: addSelect, distinct, selectRaw, selectSub, and aggregate terminals count/max/min/sum/avg. - Ordering and limits: orderByDesc, latest/oldest, inRandomOrder, limit/take/offset/skip, addGroupBy. - Joins: innerJoin/rightJoin/crossJoin, joinSub/leftJoinSub, fromSub. - Writes: REPLACE and setExpression, multi-row insert, insertOrIgnore, insertUsing (INSERT ... SELECT), insertGetId, upsert (ON DUPLICATE KEY UPDATE), updateOrInsert, increment/decrement. - Sets, locking and sub-queries: union/unionAll, lockForUpdate/sharedLock, with closures and sub-queries sharing one bound-parameter counter. Every dialect-specific spelling is produced through e_db_platform, so the builder stays portable toward future backends; e_db_platform_mysql implements the new compileInsert/compileInsertSelect/compileUpsert, the locking, random, date-part, JSON and full-text methods. e_db gains a lastInsertId() declaration used by insertGetId(). DB-less coverage in e_db_queryTest exercises every new method.
Rewrite the e_db PHPDoc so the query builder is the documented default and the legacy CRUD methods map to it: - A class-level decision guide on the e_db interface: builder first, e_db::execute() only for what the builder cannot express (now just INSERT ... SELECT, window functions and other unmodelled constructs), the deprecated CRUD methods last, and DDL helpers called out separately. - Each deprecated method (select/insert/update/delete/replace/retrieve/ count/max/gen/escape) names its builder replacement with an example; the trait and backend copies collapse to a pointer plus the @deprecated one-liner. insert() points to values() for multi-row rows and to upsert() for the ON DUPLICATE KEY UPDATE case. - resetTableList and the DDL helpers get real one-line descriptions. Documentation only; no runtime behaviour changes.
Deltik
approved these changes
Jun 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Expands the
e_db_queryfluent builder to feature parity with Laravel's query builder and Doctrine DBAL, so developers already fluent in either can adopt e107's variant with minimal friction. The goal was completeness: every commonly expected builder method now has a counterpart, including niche pieces that were previously missing (e.g.replace(),INSERT ... SELECT, upserts, sub-queries).This is additive and does not change existing behavior. The one intentional semantic choice is documented below.
What's new
WHERE family (each with an
orWhere*sibling;andWherekept as an alias):where()now accepts Closure groups, array predicates, 2-arg (col,value) and 3-arg (col,op,value) formswhereNull/whereNotNull,whereIn/whereNotIn(array or sub-query),whereBetween/whereNotBetween,whereColumn,whereLike/whereNotLike,whereExists/whereNotExists,whereNotwhereDate/whereYear/whereMonth/whereDay/whereTime), JSON helpers (whereJsonContains/whereJsonContainsKey/whereJsonLength), and full-text (whereFullText)HAVING:
having/orHaving/andHaving(value form),havingRaw/orHavingRaw,havingBetween/orHavingBetweenSELECT / aggregates:
addSelect,distinct,selectRaw,selectSub, and terminalcount/max/min/sum/avgOrdering / limit:
orderByDesc,latest,oldest,inRandomOrder,limit/take,offset/skip,addGroupByJoins:
innerJoin,rightJoin,crossJoin,joinSub/leftJoinSub,fromSubWrites:
insertOrIgnore, multi-rowvalues(),insertUsing($columns, $query)(INSERT ... SELECT),insertGetId(),upsert(),updateOrInsert(),increment/decrementSub-queries & set ops: Closure /
e_db_querysub-queries inwhereIn/whereExists/fromSub/joinSub/selectSub, plusUNION/UNION ALLLocking:
lockForUpdate,sharedLockSemantics: chained
where()appends (Laravel-style)Chained
where()calls now combine withAND(matching Laravel and DBAL), instead of the previous replace-last behavior.orWhere()adds anORterm;andWhere()is an explicit alias. This is the only behavioral change and was chosen deliberately for parity.Safety model is preserved
No loosening of the existing guarantees:
createNamedParameter()(:qbN); sub-builders share the parent's parameter counter so numbering stays unique across nested queries.e_db_filter::identifier()grammar and fail closed (throwInvalidArgumentException) rather than interpolating untrusted text.e_db_platform/e_db_platform_mysql) so the builder stays portable.Compatibility
Respects the PHP 5.6 floor restored by #5669: no reserved-word method names (
andX()/orX()rather thanand()/or()), no nullable type hints, no null-coalescing,array()syntax throughout.Docs
e_db_interfacedocblocks were trimmed to reflect the now-supported surface, and callers are steered toward the builder. The stale "not yet supported" gap-list now lists onlyINSERT ... SELECT-style window functions that remain out of scope. AlastInsertId()method was added to thee_dbinterface (both backends already implemented it).Testing
e_db_queryTest: 52 test methods / 162 assertions, all greene_db_parityTest: green (both backends satisfy the interface, incl.lastInsertId())e_library_managerTestversion-consistency check fetches a CDN URL) that fails only in the offline container; unrelated to this changeCommits
Two commits:
feat(db)(builder + platform + tests) thendocs(db)(interface docblocks and caller guidance).