Skip to content

Forms Overview ​

Modularous forms are schema-driven. The backend defines inputs; Hydrates transform them into a frontend schema; Vue components consume and render them.

What Are Hydrates? ​

Hydrates are PHP classes that sit between your module config and the frontend form. Each input type has its own Hydrate class that transforms a raw config array into a well-defined schema object the Vue side can render.

Module config  →  InputHydrator  →  XxxHydrate  →  schema  →  Vue component
{ type: 'checklist', ... }              ↓          { type: 'input-checklist', items: [...], ... }
                               resolves class name

What they do? ​

  • Set defaults — fill in missing keys (e.g. itemValue: 'id', itemTitle: 'name')
  • Change the type — convert 'checklist' (config alias) → 'input-checklist' (frontend type)
  • Load records — call a repository or connector to populate items for selectable inputs
  • Apply rules — parse rules string and add CSS classes (e.g. required)
  • Strip backend keys — remove route, model, repository, cascades, connector before the schema reaches the frontend

Why they exist? ​

The Hydrate layer decouples module config syntax from frontend schema syntax. You write concise config in PHP; the Hydrate handles enrichment, fetching, and normalization. The frontend only ever sees a clean, complete schema object.

Resolution rule ​

InputHydrator resolves the class by: studlyCase($input['type']) . 'Hydrate'

Config typeResolved class
checklistChecklistHydrate
select-scrollSelectScrollHydrate
filepond-avatarFilepondAvatarHydrate

All Hydrate classes live in src/Hydrates/Inputs/.

Render Pipeline ​

Every Hydrate runs the same pipeline when render() is called:

setDefaults()     — apply $requirements defaults
hydrate()         — set output type, enrich schema
hydrateRecords()  — load items via repository/connector
hydrateRules()    — parse rules string, add CSS classes
Arr::except()     — strip backend-only keys

Form Rendering Flow ​

  1. Module config — define inputs in your module's config.php
  2. Controller — setupFormSchema() calls InputHydrator before create/edit
  3. Inertia — hydrated schema + model are passed to the page
  4. Form.vue — receives schema and modelValue, uses useForm
  5. FormBase — flattens schema + model into flatCombinedArraySorted, iterates over each field
  6. FormBaseField — renders each field by obj.schema.type via mapTypeToComponent()
  7. Input components — receive schema props via bindSchema(obj)

Key Components ​

ComponentPurpose
Form.vueTop-level form; validation, submit, schema/model sync
FormBaseIterates over flattened schema; grid layout, slots
FormBaseFieldRenders a single field; resolves type → component
CustomFormBaseWrapper with app-specific behavior

Schema Structure ​

Each field in the schema has:

  • type — Resolved to Vue component (e.g. input-checklist, text, select)
  • name — Field name (binds to model)
  • label — Display label
  • col — Grid column span
  • rules — Validation rules
  • default — Default value

Backend-only keys (stripped before frontend): route, model, repository, cascades, connector

Config → Component Reference ​

Config typeHydrate classOutput typeVue component
assignmentAssignmentHydrateinput-assignmentVInputAssignment
autocompleteAutocompleteHydrateselect / input-select-scrollv-autocomplete
browserBrowserHydrateinput-browserVInputBrowser
chatChatHydrateinput-chatVInputChat
checkboxCheckboxHydratecheckboxv-checkbox
checklistChecklistHydrateinput-checklistVInputChecklist
checklist-groupChecklistGroupHydrateinput-checklist-groupVInputChecklistGroup
comboboxComboboxHydratecombobox / input-select-scrollv-combobox
comparison-tableComparisonTableHydrateinput-comparison-tableVInputComparisonTable
dateDateHydrateinput-dateVInputDate
fileFileHydrateinput-fileVInputFile
filepond-avatarFilepondAvatarHydrateinput-filepond-avatarVInputFilepondAvatar
form-tabsFormTabsHydrateinput-form-tabsVInputFormTabs
imageImageHydrateinput-imageVInputImage
jsonJsonHydrategroup(group layout)
json-repeaterJsonRepeaterHydrateinput-repeaterVInputRepeater
payment-servicePaymentServiceHydrateinput-payment-serviceVInputPaymentService
pricePriceHydrateinput-priceVInputPrice
processProcessHydrateinput-processVInputProcess
radio-groupRadioGroupHydrateinput-radio-groupVInputRadioGroup
repeaterRepeaterHydrateinput-repeaterVInputRepeater
select-scrollSelectScrollHydrateinput-select-scrollVInputSelectScroll
spreadSpreadHydrateinput-spreadVInputSpread
switchSwitchHydrateinput-switchVInputSwitch
tagTagHydrateinput-tagVInputTag
taggerTaggerHydrateinput-taggerVInputTagger

FormBase Slots ​

FormBase provides slots for customization:

  • form-top, form-bottom — Form-level
  • {type}-top, {type}-bottom — By schema type (e.g. input-checklist-top)
  • {key}-top, {key}-bottom — By field name
  • {type}-item, {key}-item — Override field rendering

Adding a New Input ​

  1. PHP — Create src/Hydrates/Inputs/{Studly}Hydrate.php extending InputHydrate
    • Set $input['type'] = 'input-{kebab}' in hydrate()
    • Define $requirements for default schema keys
  2. Vue — Create vue/src/js/components/inputs/{Studly}.vue
    • Use useInput, makeInputProps, makeInputEmits from @/hooks
    • Component auto-registers as VInput{Studly} via includeFormInputs glob
  3. Registry (optional) — Add to hydrateTypeMap in registry.js for explicit mapping

See the create-input-hydrate and create-vue-input commands.