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#

modules/cabana/testdata/docs/controllers/posts/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#

modules/cabana/testdata/docs/models/post/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:

modules/cabana/example_test.go#ExampleCompileList
// 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:

modules/cabana/testdata/docs/controllers/posts/config_filter.yaml
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.