You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
writeable() and linkable() clobber each other's per-action writeability; declaration order silently decides behaviour #33
On a RelationshipField, writeable() and linkable() each set their own capability and overwrite the shared base writeability, so whichever is declared last silently wins — and the field can end up in a state that contradicts itself.
Why
// RelationshipField::linkable()$this->linkActions[Action::CREATE] = $create;
$this->linkActions[Action::EDIT] = $edit;
returnparent::writeable($create, $edit); // <-- also sets base writeability// RelationshipField::writeable()$this->createActions[Action::CREATE] = $create;
$this->createActions[Action::EDIT] = $edit;
returnparent::writeable($create, $edit); // <-- same
Each writes its own array, but both write the same base flags. Declaring both with different per-action arguments therefore leaves the base flags reflecting only the later call.
Reproduction
$field = $definition->relationship('child', ChildDefinition::class)->one()
->writeable(false, true) // create: no, edit: yes
->linkable(true, false); // create: yes, edit: no
declaration order
action
isWriteable()
canCreateNewChildren()
canLinkExistingEntities()
writeable(false,true) → linkable(true,false)
create
true
false
true
edit
false
true
false
linkable(true,false) → writeable(false,true)
create
false
false
true
edit
true
true
false
Note row 2: the field reports not writeable on edit while simultaneously reporting it can create new children on edit. Nothing can act on that coherently, and reversing the two lines produces the mirror image.
Why it matters
This is not hypothetical: writeable(false, true)->linkable(true, false) is exactly how a real to-many relationship was declared downstream (opt-in campaign texts in Eukles), and the surprising behaviour there — a PUT returning 200 while silently discarding the child edit — traces back to this interaction.
The two methods look independent and read as complementary. Nothing in the signatures or docblocks hints that order matters.
Possible directions
Keep the base flags as the union of the two capabilities rather than the last writer's arguments; or
track base writeability separately from linkActions/createActions so neither method clobbers it; or
if the current behaviour is deliberate, document the ordering requirement on both methods and consider throwing when both are declared with conflicting per-action arguments.
Activity