feat: Add wrapWhere method to group existing WHERE conditions#250
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 2.x #250 +/- ##
=========================================
Coverage 95.55% 95.55%
- Complexity 2032 2034 +2
=========================================
Files 137 137
Lines 5755 5764 +9
=========================================
+ Hits 5499 5508 +9
Misses 256 256 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds a wrapWhere() primitive to WhereTrait that wraps all currently registered WHERE tokens in a single nested AND-group, so subsequent where/orWhere calls are appended alongside (not inside) the previous conditions. The primary motivation is enabling ORM scopes (e.g. soft-delete) to defend themselves against a later user-supplied orWhere that would otherwise break out of the intended boolean group.
Changes:
- Add
WhereTrait::wrapWhere()that prepends['AND','(']and appends['','')']around existing where tokens (no-op when empty). - Add 3 unit tests covering token-layout (empty no-op, mixed AND/OR wrapping, scope-protection scenario).
- Add 15 functional tests covering SQL output for various combinations (AND chains, mixed AND/OR,
whereNot, closure groups before/after, double wrap, stacked scopes,orWhereNot, array@or,BETWEEN/IN, leadingorWhere).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/Query/Traits/WhereTrait.php | Introduces the wrapWhere() method that brackets existing where tokens into a single AND-group. |
| tests/Database/Unit/Query/Tokens/SelectQueryTest.php | Token-level tests asserting exact structure after wrapWhere(). |
| tests/Database/Functional/Driver/Common/Query/SelectQueryTest.php | Driver-wide functional tests asserting compiled SQL across many condition shapes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
🔍 What was changed
Added
wrapWhere()toWhereTrait. The method collapses all currently-registeredWHEREtokens into a single nested AND-group, leaving the where state with exactly one top-level token — the enclosing group. Subsequentwhere()/andWhere()/orWhere()/...calls append at the top level alongside that group, no longer inside it.Behavior:
wrapWhere()before any condition is registered does nothing.AND,OR,NOT,BETWEEN,IN, closure groups,@or/@andarray sugar) are kept as-is inside the new group.Note
The method is a low-level primitive intended for use by ORM scope mechanisms (and any other code that needs to defensively enclose its conditions). It is safe to expose publicly but is not the canonical way to build a nested WHERE — for that, keep using
where(function ($q) { ... }).🤔 Why?
Today an ORM scope (e.g.
SoftDeleteScope) adds its conditions at the top level of the WHERE clause. As soon as user code attachesorWhere(...)later, the scope and the user predicate join the same flat boolean chain:There was no way for a scope to enclose itself (or the user's prior conditions) in parentheses without a heavier rework — the only existing nesting primitive is the closure form of
where(), which can only group conditions being added now, not conditions already registered.wrapWhere()fills exactly that gap. With it, a scope'sapply()can do:producing
This unlocks an isolation pattern for stacked scopes (e.g.
SoftDelete+ multi-tenancy + role visibility) without introducing a new scope interface, without changingScopeInterface::apply(), and without touching theSelectQuerybuild pipeline — see the companion change incycle/orm.📝 Checklist
Tests cover:
Tests/Unit/Query/Tokens/SelectQueryTest): no-op on empty state, exact token layout after wrapping a mixed AND/OR chain, scope-protection scenario.Common/Query/SelectQueryTest): plain AND chain, mixed AND/OR,whereNot, wrapping an already-closure-nested group, attaching a closure group afterwrapWhere(), doublewrapWhere(), stacked scope emulation (two layers),orWhereNotafter wrap, array syntax with@or,BETWEEN/INoperators, and a chain that starts withorWhere.Full SQLite functional suite: 662 tests, no regressions.
📃 Documentation
Inline phpdoc on
WhereTrait::wrapWhere()with a worked example and a description of the typical use case (scope isolation). No separate doc page is required for a single primitive; the consumer-facing documentation will live in the ORM-side PR that wires this into the scope mechanism.