- docs/backend: admin controllers, forms, lists and filters, relation manager, users and permissions, settings, partials and widgets, admin SPA - docs/services: storage, outbound HTTP, realtime, Web Push, search, parity testing and the Frontend and AJAX (not provided) page - Examples for cabana (with testdata/docs YAML), fetchguard, lighthouse and its centrifugo driver, flare, beachcomber and typesense, tide; lighthouse and beachcomber TestDocs* regions run on their Postgres harnesses - concept map rows link their guide pages and the not-provided rows the Frontend and AJAX page; index lists Backend, Database and Services - TestDocsRequiredPages asserts the D-08 section order
5.1 KiB
title, description, section, order
| title | description | section | order |
|---|---|---|---|
| Forms | Describe admin forms in config_form.yaml and fields.yaml, with the supported field types, spans, tabs, dropdown options and create or update contexts. | backend | 20 |
Forms
The Form behaviour's config_form.yaml and the model's fields.yaml keep their WinterCMS shape. cabana compiles them strictly at boot: an unknown key, field type, span or size stops the start-up with an error naming the file and the field, so a WinterCMS option that SummerCMS does not implement is never ignored silently.
config_form.yaml
The controller's form configuration names the fields file with a WinterCMS path, the model class, and where the SPA goes after a save:
name: acme.blog::lang.posts.form
form: ~/plugins/acme/blog/models/post/fields.yaml
modelClass: Post
defaultRedirect: acme/blog/posts
create:
redirect: acme/blog/posts/update/:id
redirectClose: acme/blog/posts
update:
redirect: acme/blog/posts
redirectClose: acme/blog/posts
modelClass must equal the controller's pact.AdminController.ModelName. The ~/plugins/<vendor>/<plugin>/ prefix points into the plugin's own embedded tree.
fields.yaml
fields:
title:
label: acme.blog::lang.posts.title_column
type: text
span: left
required: true
slug:
label: acme.blog::lang.posts.slug
type: text
span: right
context: update
comment: acme.blog::lang.posts.slug_comment
status:
label: acme.blog::lang.posts.status
type: dropdown
span: left
options:
draft: acme.blog::lang.posts.draft
published: acme.blog::lang.posts.published
published:
label: acme.blog::lang.posts.published
type: switch
span: right
content:
label: acme.blog::lang.posts.content
type: textarea
size: large
tab: acme.blog::lang.posts.tab_content
The compiled schema keeps the fields in file order, with their labels as translation keys until a request asks for them in its locale:
fsys := os.DirFS("testdata/docs")
form, err := cabana.CompileForm("acme.blog", PostsController{}, fsys)
if err != nil {
fmt.Println(err)
return
}
for _, f := range form.Fields {
fmt.Printf("%s %s span=%q tab=%q required=%v options=%d\n", f.Name, f.Type, f.Span, f.Tab, f.Required, len(f.Options))
}
// Output:
// title text span="left" tab="" required=true options=0
// slug text span="right" tab="" required=false options=0
// status dropdown span="left" tab="" required=false options=2
// published switch span="right" tab="" required=false options=0
// content textarea span="" tab="acme.blog::lang.posts.tab_content" required=false options=0
Field types
| Type | Renders |
|---|---|
text, textarea, number |
Text inputs. |
checkbox, switch |
Booleans. |
dropdown |
A select. Options are a map in the YAML, or the name of a method the controller answers through pact.DropdownOptionsProvider. |
relation |
A belongsTo or belongsToMany picker; see Relation manager. |
relation-manager |
An embedded list of related records; see Relation manager. |
widget |
A plugin custom element with a server action; see Partials and widgets. |
partial |
A server-rendered template; see Partials and widgets. |
The WinterCMS widgets that are not in this list (the rich editor, the media finder, the repeater, the file upload and the others) are not provided. A field with one of those types stops the start-up.
Field options
A field takes label, comment, type, required, default, tab, span (left, right, full, auto, row), size (tiny, small, large, huge, giant), context, attributes (scalar HTML attributes for the input), options and emptyOption, plus nameFrom and relation on relation fields. WinterCMS keys outside this set, such as readOnly, disabled, trigger or dependsOn, are refused.
context: update shows a field only on the update form, and context: create only on the create form; a list of contexts is also accepted. The context is enforced on the server too: a field that is hidden on a form is never written by that form's save, whatever the request body holds.
What a save may write
The form's writable fields are bound to model columns at boot. A save passes only those fields that are also in the model's Fillable list, drops unknown keys, case variants and nested objects, and fills the model with lagoon.Fill (see Models). The model's validation rules (a Rules method returning lagoon.Validate rule strings) and the form's required flags are checked in the save's transaction, and a failure is a 422 with messages per field. A value that does not fit its column is also a 422 on that field.