Repository navigation
Conversation
With `nested_mutations_scope_to_parent` enabled, nested delete, disconnect, update and upsert only act on models related to the parent and throw an error otherwise. Upsert still creates a model when the given ID exists nowhere. Resolves #2744 Supersedes #2454 and #2751 Co-authored-by: Lucas Lenz <lucas.lenz@mailbox.org> 🤖 Generated with Claude Code
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
1 open finding
What changed in this PR
Adds an opt-in safety feature to scope nested mutation operations (delete, disconnect, update, upsert) to the parent relation, preventing cross-parent modifications unless explicitly allowed (with v7 planned to make this default).
Changes:
- Introduces
nested_mutations_scope_to_parentconfig andParentRelationScopeenforcement utilities. - Updates nested mutation execution to scope
delete/disconnectand to pass parent relations intoUpdateModel/UpsertModelfor scoped lookup. - Adds/updates integration tests and updates docs, CHANGELOG, and UPGRADE guidance.
| File | Description |
|---|---|
| tests/Integration/Schema/Directives/UpsertDirectiveTest.php | Adds coverage for scoped nested upsert via identifying columns |
| tests/Integration/Execution/MutationExecutor/MorphToManyTest.php | Adds scoped upsert negative test for MorphToMany |
| tests/Integration/Execution/MutationExecutor/MorphOneTest.php | Adds scoped upsert negative test for MorphOne |
| tests/Integration/Execution/MutationExecutor/MorphManyTest.php | Adds scoped delete/upsert negative tests for MorphMany |
| tests/Integration/Execution/MutationExecutor/HasOneTest.php | Adds scoped delete/update/upsert negative tests for HasOne |
| tests/Integration/Execution/MutationExecutor/HasManyTest.php | Adds scoped delete/disconnect/update/upsert tests for HasMany |
| tests/Integration/Execution/MutationExecutor/BelongsToTest.php | Verifies BelongsTo remains unscoped even when option enabled |
| tests/Integration/Execution/MutationExecutor/BelongsToManyTest.php | Adds scoped delete/update/upsert tests for BelongsToMany |
| src/lighthouse.php | Adds new config option with inline documentation |
| src/Schema/Directives/UpsertManyDirective.php | Passes parent relation through to upsert execution |
| src/Schema/Directives/UpsertDirective.php | Passes parent relation through to upsert execution |
| src/Schema/Directives/UpdateManyDirective.php | Passes parent relation through to update execution |
| src/Schema/Directives/UpdateDirective.php | Passes parent relation through to update execution |
| src/Execution/Arguments/UpsertModel.php | Adds scoped lookup support via parent relation |
| src/Execution/Arguments/UpdateModel.php | Adds scoped lookup support via parent relation |
| src/Execution/Arguments/ParentRelationScope.php | New helper implementing scoping rules and consistent error |
| src/Execution/Arguments/NestedOneToOne.php | Routes nested delete/update/upsert through scoping logic |
| src/Execution/Arguments/NestedOneToMany.php | Routes nested disconnect/delete/update/upsert through scoping logic |
| src/Execution/Arguments/NestedManyToMany.php | Routes nested delete/update/upsert through scoping logic |
| docs/master/eloquent/nested-mutations.md | Documents new config and which operations remain unscoped |
| docs/6/eloquent/nested-mutations.md | Backports documentation for v6 users |
| UPGRADE.md | Documents v7 behavior change (option removed; always scoped) |
| CHANGELOG.md | Notes new opt-in config option |
🧠 Review effort: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
🤖 Generated with Claude Code
`destroy()` and `detach()` accepted a scalar, so `delete: ID` worked before. 🤖 Generated with Claude Code
🤖 Generated with Claude Code
Callers no longer branch on applies(), and unscoped lookups run the same queries as before the option existed. 🤖 Generated with Claude Code
With `nested_mutations_scope_to_parent`, `@delete(relation: ...)` deleted models of other parents. 🤖 Generated with Claude Code
🤖 Generated with Claude Code
🤖 Generated with Claude Code
🤖 Generated with Claude Code
An array identifying value crashed with "Array to string conversion". 🤖 Generated with Claude Code
|
🤖 This branch conflicts with master and needs an update before it can merge. The local review is done and its fixes are pushed, including scoping for the nested |
# Conflicts: # src/Schema/Directives/DeleteDirective.php

Resolves #2744
Nested
delete,disconnect,updateandupsertact on models of other parents.The new opt-in option
nested_mutations_scope_to_parentlimits them to related models, and v7 makes it the default.With the option enabled, any ID not related to the parent causes the same error
The error names the rejected IDs, e.g. "Cannot modify a model that is not related to the given parent: 3, 7."
Missing and foreign IDs get the same error, so a client cannot probe which IDs exist elsewhere.
All IDs of a
deleteordisconnectare checked before any model changes.upsertis the exception: an ID that exists nowhere still creates a model, so client-generated IDs keep working.An ID or
identifyingColumnsmatch that exists outside the relation causes the error.connect, sync and BelongsTo update/upsert stay unscoped
Their purpose is to establish new relationships.
The security section of the nested mutations docs now names them as an authorization concern.
Disabled by default, so v6 behaviour is unchanged
UPGRADE.mddescribes the v7 change: the option is removed and scoping always applies.Clients that move models between parents through
updateorupsertmust useconnect.Builds on two closed PRs
UpsertModeland the upsert tests.Breaking changes
None in v6.
v7 makes scoping the default, see
UPGRADE.md.Changelog
Added
nested_mutations_scope_to_parentto limit nesteddelete,disconnect,updateandupsertto models related to the parent Nested mutations allow operations on records outside parent relationship scope #2744🤖 Generated with Claude Code