Panel form slices on the create pages and settings forms (spec 0088) - #2742
Open
glennjacobs wants to merge 3 commits into
Open
glennjacobs wants to merge 3 commits into
glennjacobs wants to merge 3 commits into
Conversation
… 0088) Composes a model's registered form slices into the panel's plain forms, so the same FormSlice class an add-on wrote for a drafted edit page also serves the create pages and the settings edit forms. A plain form carries only what it posts, so a slice takes part only when a component bound it: its keys join the form on bind, its rules apply when its keys are present, and its commit runs for the namespaces the input holds, after the form's action inside one transaction. - SliceSet extracted from ComposedDraftResource and shared by both composers. - FormSlices service: rules() under `sometimes`, values() for seeding, commit(), and save() wrapping an action and the commit in a transaction. - ValidatesFormSlices request trait merges slice rules into the six create and thirteen settings requests, keeps slice keys out of validated(), and hands them over through sliceInput(). - Create controllers pass formSliceValues for a fresh record; the shared prop now resolves slices for any model, not only draftable ones. - usePanelForm wraps Inertia's useForm and provides a SliceForm with claim-on-bind; every create page and settings edit page swaps to it. - ProductAttributeSlice tolerates an unsaved product. - The example add-on's loyalty card also sits on the customer create page, with an end-to-end test through the plain store route. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
glennjacobs
added this pull request to stack #2743
September 16, 2026 21:28
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
Stacked on #2741 (spec 0086). Implements spec 0088, included in this PR: the panel's plain forms compose the same
FormSlicecontract the drafted edit pages do, so one slice class serves both. This is the answer to the review on #2741: no second contract.The stance. An add-on adds fields under a namespace it owns, on any first-party form's save, and never touches the form's own fields. The form kind decides the plumbing, not the contract.
What differs from a drafted page. A plain form carries only what it posts, so a slice takes part only when a component bound it:
useFormSliceon a plain page seeds that namespace's keys fromformSliceValuesinto the form's data and defaults. A page nobody extends posts exactly what it did before; product creation does not suddenly carry the product's channel rows.sometimes, so an unbound namespace cannot fail validation; within a bound namespace the slice's rules apply in full.commit()receives the created record inside the store's transaction.Changes
Forms\SliceSet: the prefixing composer, extracted fromComposedDraftResourceand shared.Forms\FormSlices:rules(),values(),commit(), andsave(input, action)which runs the action and the commit in one transaction.Http\Requests\Concerns\ValidatesFormSlices: merges slice rules into a request's own (one validation round), keeps slice keys out ofvalidated()so requests that pass validated input to actions wholesale never leak them, and exposes them viasliceInput(). Applied to the six create requests and thirteen settings requests.FormSlices::save(); create controllers passformSliceValuesfor a fresh record. The shared prop now resolves slices for any model with slices, not only draftable ones.usePanelForm: wraps Inertia'suseForm, returns it unchanged, and provides aSliceFormwith claim-on-bind. All nineteen plain-form pages swap to it. Exported on@lunarphp/panel.ProductAttributeSlicetolerates an unsaved product (no type, no mapping).customers.create:main:after, same slice, same component. README section extended.Out of scope, by design: the settings index pages' inline create dialogs (no slot zone to bind in), the roles form (a Spatie model), auth and account forms, and the order view.
Related
sometimesrules, commit for submitted namespaces only). Merge it after this stack lands.Verification
vendor/bin/pest --testsuite panel --parallel: 967 passed, including newFeature/Forms/FormSlicesTest(settings update, create, one-round validation under the prefixed key, unbound namespace untouched, rollback on a failing slice commit) and the example add-on's create-page testvendor/bin/phpstan analyse: no errors;vendor/bin/pint: cleannpm test(352 passed),npm run type-check,npm run build; example add-on builds through the workspace🤖 Generated with Claude Code