Harden tree operations against global scopes; fix zero-offset SQL#3
Merged
Conversation
Port and adapt fixes from upstream kalnoy/nestedset for issues that
affect models defining global scopes, plus a SQL generation bug.
- getNodeData() now selects only lft/rgt and returns them in a known
order, so a scope's extra columns can no longer corrupt the
[$lft, $rgt] read during a move (upstream issue #513).
- isBroken(), countErrors(), getTotalErrors(), fixTree() and
fixSubtree() accept an optional callback to customise the query,
most usefully to drop an interfering global scope (upstream PR #536).
- withDepth() honours global scopes removed from the outer query, so
withoutGlobalScope(...)->withDepth() counts the same ancestors in the
depth subquery (upstream PR #300).
- columnPatch() renders a zero offset as "+0" instead of concatenating
it onto the column ("_rgt"0), which is invalid SQL on stricter
drivers such as SQL Server (upstream PR #595).
Also expands test coverage for previously untested public query scopes
and predicates (leaves, hasParent/hasChildren, getNextNode/getPrevNode,
inclusive descendant/ancestor queries, inclusive predicates, etc.).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
Ports and adapts several fixes from upstream
kalnoy/nestedsetthat affect models defining global scopes, plus a SQL-generation bug. Each change is paired with regression tests, and the previously thin coverage of read-side query scopes has been expanded.All changes live in
src/QueryBuilder.php. The full suite passes (114 tests), Pint is clean, and PHPStan reports no errors.Fixes
getNodeData()selects onlylft/rgtand returns them in a fixed orderaddSelect) used to leak into the positional[$lft, $rgt]read inmoveNode, throwing aTypeErroron$rgt - $lft.$callbackonisBroken(),countErrors(),getTotalErrors(),fixTree(),fixSubtree()fn ($q) => $q->withoutGlobalScopes(). Backwards compatible.withDepth()honours scopes removed from the outer queryModel::withoutGlobalScope(...)->withDepth()now counts the same ancestor set in the depth subquery.columnPatch()renders a zero offset as+0"_rgt"0, which is invalid SQL on stricter drivers such as SQL Server.Tests
tests/GlobalScopeTest.php— covers all four fixes, including directcolumnPatchassertions and a demonstration that an interfering global scope disrupts the integrity checks until the callback drops it. Verified to genuinely fail when the source fixes are reverted.tests/QueryScopesTest.php— fills a coverage gap on read-side public API:whereIsLeaf/leaves,hasParent/hasChildren,getNextNode/getPrevNode, inclusive descendant/ancestor queries, inclusive predicates,getBounds,prependToNode, and OR/negation query branches.SelectScopedCategory,FilteredCategory) model the global-scope scenarios.Deliberately out of scope
Upstream PR #514 also blanket-swapped
newQuery()→newQueryWithoutScopes()indescendants(),ancestors(),newNestedSetQuery()andnewScopedQuery(). That changes soft-delete/scoping semantics package-wide (e.g.siblings()would start returning trashed rows), so it's left out as a separate maintainer decision. The targetedgetNodeDatafix resolves the #513 crash without that risk.🤖 Generated with Claude Code