Backend
Lists and filters
Describe admin lists in config_list.yaml and columns.yaml, with search, sorting, pagination options and switch, date range and scope filters.
On this page
The List behaviour's config_list.yaml, the model's columns.yaml and the Filter widget's config_filter.yaml keep their WinterCMS shape. cabana compiles them at boot and runs every list query itself, so search, sort and filter parameters from the request apply only to what the YAML declares.
config_list.yaml#
list: ~/plugins/acme/blog/models/post/columns.yaml
modelClass: Post
title: acme.blog::lang.posts.title
recordUrl: acme/blog/posts/update/:id
recordsPerPage: 20
perPageOptions: [20, 50, 100]
showCheckboxes: true
defaultSort:
column: published_at
direction: desc
filter: config_filter.yaml
toolbar:
buttons: [create, delete]
search:
prompt: backend::lang.list.search_prompt
| Key | Sets |
|---|---|
list |
The columns file, with a WinterCMS ~/plugins/... path into the plugin. |
modelClass |
Must equal the controller's model name. |
title, noRecordsMessage |
Translation keys shown by the SPA. |
recordUrl |
Where a click on a row goes; :id is replaced. |
recordsPerPage, perPageOptions |
The page size and the sizes a user may pick. |
showCheckboxes, showSetup, showSorting, showSearch |
Which list controls appear. |
defaultSort |
column and direction of the initial order. |
toolbar |
buttons (the built-in create and delete and registered actions) and search.prompt. |
filter |
The filter file, relative to the controller's directory. |
headerPartial |
A server-rendered strip above the list; see Partials and widgets. |
columns.yaml#
columns:
title:
label: acme.blog::lang.posts.title_column
searchable: true
published:
label: acme.blog::lang.posts.published
type: switch
published_at:
label: acme.blog::lang.posts.published_at
type: datetime
A column takes label, type (text, datetime or switch), searchable (default false) and sortable (default true, as in WinterCMS), and, for a column read through a relation, relation and select. Other WinterCMS column types and options are refused at boot. The compiled list keeps the columns in file order:
// The plugin embeds its controllers/ and models/ trees; the example reads
// the same files from testdata.
fsys := os.DirFS("testdata/docs")
list, err := cabana.CompileList("acme.blog", PostsController{}, fsys)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(list.Title, list.RecordsPerPage, list.PerPageOptions, list.ToolbarButtons)
for _, c := range list.Columns {
fmt.Printf("column %s type=%q searchable=%v sortable=%v\n", c.Key, c.Type, c.Searchable, c.Sortable)
}
for _, f := range list.Filters {
fmt.Printf("filter %s type=%s column=%s\n", f.Name, f.Type, f.Column)
}
// Output:
// acme.blog::lang.posts.title 20 [20 50 100] [create delete]
// column title type="" searchable=true sortable=true
// column published type="switch" searchable=false sortable=true
// column published_at type="datetime" searchable=false sortable=true
// filter published type=switch column=published
// filter published_at type=daterange column=published_at
Search runs over the searchable columns only; sort accepts only sortable columns. A request that names any other column is refused, never passed to SQL.
Filters#
config_filter.yaml lists scopes. Three filter types are supported:
scopes:
published:
label: acme.blog::lang.posts.published
type: switch
column: published
published_at:
label: acme.blog::lang.posts.published_at
type: daterange
column: published_at
| Type | Filters |
|---|---|
switch |
A boolean column. With options, the two values are the options' keys, kept as typed scalars. |
daterange |
A date or timestamp column between two dates. |
scope (a scope key with modelClass and nameFrom) |
Records by a model-backed choice. The model implements pact.FilterScope, whose FilterScopes lists the scope names it answers, and pact.FilterOptions for the choices the SPA loads. |
WinterCMS conditions SQL fragments are not supported; use a column or a scope the model implements. A scope filter whose name the model does not list in FilterScopes stops the start-up, so request text can never select another method.
Scoping every list#
To restrict which records an administrator sees at all, implement pact.ListExtendQuery on the controller. It receives the list query before search, filters and pagination are applied, so the restriction holds for every request. See Admin controllers for the other hooks.