Conversation
The write path has had a named seam since ADR 0021 — a field shapes its own
value on the way out — but it had exactly two hosts: Form::save() through
SaveHandler, and an editable table cell through updateTableCell(). An action
modal was not one of them. submitActionModal() / callMountedAction() read
mountedActions.{depth}.data and handed it to the callback untouched, so the
same schema wrote different values depending on which host persisted it:
Select::dehydrateState()'s documented '' -> null rule simply did not apply
inside a modal, and a cleared numeric field reached the column as '' —
SQLSTATE[22007] "Incorrect decimal value: ''" on any strict MySQL.
The schema walk moves out of SaveHandler into Forms\Runtime\StateDehydrator,
the canonical owner both hosts now call, and the action runtime gains the seam
next to the validation one it already had: dehydrateMountedActionFormData(),
a no-op in core, overridden by the wire-forms bridge, so a form-free host is
unaffected. Wizards dehydrate every step, not only the one on screen at submit;
repeater children are dehydrated per item. Footer actions are deliberately
excluded — they read the form mid-edit and write back into the same bag, so
dehydrating there would hand the callback a value the form no longer holds, and
would run a FileUpload's store on a form the user never submitted.
TextInput had no dehydration at all, so numeric() was a rendering hint and
nothing more — this half reached Form::save() too. A field whose HTML type is
number now stores null when it is left empty; every other type is untouched on
purpose, because '' is a legitimate string and nulling it would break a
non-nullable text column rather than save it.
The tests are mutation-checked: removing either call site fails them.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
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.
The write path has had a named seam since ADR 0021 — a field shapes its own value on the way out — but it had exactly two hosts: Form::save() through SaveHandler, and an editable table cell through updateTableCell(). An action modal was not one of them. submitActionModal() / callMountedAction() read mountedActions.{depth}.data and handed it to the callback untouched, so the same schema wrote different values depending on which host persisted it: Select::dehydrateState()'s documented '' -> null rule simply did not apply inside a modal, and a cleared numeric field reached the column as '' — SQLSTATE[22007] "Incorrect decimal value: ''" on any strict MySQL.
The schema walk moves out of SaveHandler into Forms\Runtime\StateDehydrator, the canonical owner both hosts now call, and the action runtime gains the seam next to the validation one it already had: dehydrateMountedActionFormData(), a no-op in core, overridden by the wire-forms bridge, so a form-free host is unaffected. Wizards dehydrate every step, not only the one on screen at submit; repeater children are dehydrated per item. Footer actions are deliberately excluded — they read the form mid-edit and write back into the same bag, so dehydrating there would hand the callback a value the form no longer holds, and would run a FileUpload's store on a form the user never submitted.
TextInput had no dehydration at all, so numeric() was a rendering hint and nothing more — this half reached Form::save() too. A field whose HTML type is number now stores null when it is left empty; every other type is untouched on purpose, because '' is a legitimate string and nulling it would break a non-nullable text column rather than save it.
The tests are mutation-checked: removing either call site fails them.