Skip to content

feat(db): expand e_db_query to Laravel and Doctrine DBAL parity#5763

Merged
Deltik merged 2 commits into
masterfrom
e107help/db-docs
Jun 14, 2026
Merged

feat(db): expand e_db_query to Laravel and Doctrine DBAL parity#5763
Deltik merged 2 commits into
masterfrom
e107help/db-docs

Conversation

@e107help

@e107help e107help Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Expands the e_db_query fluent 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; andWhere kept as an alias):

  • where() now accepts Closure groups, array predicates, 2-arg (col, value) and 3-arg (col, op, value) forms
  • whereNull / whereNotNull, whereIn / whereNotIn (array or sub-query), whereBetween / whereNotBetween, whereColumn, whereLike / whereNotLike, whereExists / whereNotExists, whereNot
  • Date helpers (whereDate / whereYear / whereMonth / whereDay / whereTime), JSON helpers (whereJsonContains / whereJsonContainsKey / whereJsonLength), and full-text (whereFullText)

HAVING: having / orHaving / andHaving (value form), havingRaw / orHavingRaw, havingBetween / orHavingBetween

SELECT / aggregates: addSelect, distinct, selectRaw, selectSub, and terminal count / max / min / sum / avg

Ordering / limit: orderByDesc, latest, oldest, inRandomOrder, limit/take, offset/skip, addGroupBy

Joins: innerJoin, rightJoin, crossJoin, joinSub / leftJoinSub, fromSub

Writes: insertOrIgnore, multi-row values(), insertUsing($columns, $query) (INSERT ... SELECT), insertGetId(), upsert(), updateOrInsert(), increment / decrement

Sub-queries & set ops: Closure / e_db_query sub-queries in whereIn/whereExists/fromSub/joinSub/selectSub, plus UNION / UNION ALL

Locking: lockForUpdate, sharedLock

Semantics: chained where() appends (Laravel-style)

Chained where() calls now combine with AND (matching Laravel and DBAL), instead of the previous replace-last behavior. orWhere() adds an OR term; 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:

  • Values are always bound via createNamedParameter() (:qbN); sub-builders share the parent's parameter counter so numbering stays unique across nested queries.
  • Identifiers are validated against the e_db_filter::identifier() grammar and fail closed (throw InvalidArgumentException) rather than interpolating untrusted text.
  • Every dialect-specific spelling routes through the platform seam (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 than and()/or()), no nullable type hints, no null-coalescing, array() syntax throughout.

Docs

e_db_interface docblocks were trimmed to reflect the now-supported surface, and callers are steered toward the builder. The stale "not yet supported" gap-list now lists only INSERT ... SELECT-style window functions that remain out of scope. A lastInsertId() method was added to the e_db interface (both backends already implemented it).

Testing

  • e_db_queryTest: 52 test methods / 162 assertions, all green
  • e_db_parityTest: green (both backends satisfy the interface, incl. lastInsertId())
  • Full unit suite green except one network-dependent test (e_library_managerTest version-consistency check fetches a CDN URL) that fails only in the offline container; unrelated to this change

Commits

Two commits: feat(db) (builder + platform + tests) then docs(db) (interface docblocks and caller guidance).

e107help added 2 commits June 14, 2026 16:20
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 Deltik merged commit 3b976b9 into master Jun 14, 2026
48 checks passed
@Deltik Deltik deleted the e107help/db-docs branch June 14, 2026 04:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants