Skip to content

Fix relation override scope lost when Select is cloned#569

Merged
roxblnfk merged 2 commits into
2.xfrom
fix-clone-loses-override-scope
Jun 2, 2026
Merged

Fix relation override scope lost when Select is cloned#569
roxblnfk merged 2 commits into
2.xfrom
fix-clone-loses-override-scope

Conversation

@roxblnfk

@roxblnfk roxblnfk commented Jun 2, 2026

Copy link
Copy Markdown
Member

🔍 What was changed

JoinableLoader::withContext() decided the relation scope from the presence
of the scope key in the passed $options argument. During a clone the
loader tree is re-parented via AbstractLoader::__clone(), which calls
withContext($this) with no options, so the code always fell into the else
branch and re-applied the source scope — silently discarding any override
set through load(..., scope: false) or load(..., scope: new QueryScope(...)).

Because fetchOne()/findOne() clone internally ((clone $this)->limit(1)), and
addGroupByPK() clones for paginated joined queries, the override never
reached the executed query. fetchAll() on a POSTLOAD relation does not clone,
which is why the bug escaped the existing tests.

Drive the scope decision off the persisted options['scope'] instead. That
option survives cloning via the $options + $this->options merge, so the
override (false / null / ScopeInterface / string) is now preserved across
clones, while an absent key or true still resolves to the source scope.

roxblnfk added 2 commits June 2, 2026 14:21
A relation `load()` override scope (`scope: false` or a custom QueryScope)
is silently dropped when the Select is cloned. fetchOne()/findOne() clone
internally via `(clone $this)->limit(1)`, so the override never reaches the
executed query and the relation's source scope is wrongly re-applied.

These tests are currently red and pin the expected behavior:
 - testDisabledRelationScopeSurvivesClone
 - testFetchOneKeepsDisabledRelationScope
 - testFetchOneKeepsCustomRelationScopeOverride
JoinableLoader::withContext() decided the relation scope from the presence
of the `scope` key in the *passed* `$options` argument. During a clone the
loader tree is re-parented via AbstractLoader::__clone(), which calls
withContext($this) with no options, so the code always fell into the `else`
branch and re-applied the source scope — silently discarding any override
set through load(..., scope: false) or load(..., scope: new QueryScope(...)).

Because fetchOne()/findOne() clone internally ((clone $this)->limit(1)), and
addGroupByPK() clones for paginated joined queries, the override never
reached the executed query. fetchAll() on a POSTLOAD relation does not clone,
which is why the bug escaped the existing tests.

Drive the scope decision off the persisted `options['scope']` instead. That
option survives cloning via the `$options + $this->options` merge, so the
override (false / null / ScopeInterface / string) is now preserved across
clones, while an absent key or `true` still resolves to the source scope.
@roxblnfk roxblnfk added the type:bug Bug label Jun 2, 2026
@roxblnfk roxblnfk added this to Cycle Jun 2, 2026
@github-project-automation github-project-automation Bot moved this to In Progress in Cycle Jun 2, 2026
@roxblnfk roxblnfk added this to the 2.17.x milestone Jun 2, 2026
@codecov

codecov Bot commented Jun 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.63%. Comparing base (9f35880) to head (fe8736b).

Additional details and impacted files
@@             Coverage Diff              @@
##                2.x     #569      +/-   ##
============================================
+ Coverage     91.61%   91.63%   +0.02%     
+ Complexity     2017     2014       -3     
============================================
  Files           131      131              
  Lines          5234     5235       +1     
============================================
+ Hits           4795     4797       +2     
+ Misses          439      438       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@roxblnfk roxblnfk merged commit 8109639 into 2.x Jun 2, 2026
27 of 30 checks passed
@roxblnfk roxblnfk deleted the fix-clone-loses-override-scope branch June 2, 2026 10:42
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Cycle Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant