Skip to content

Configuration

Valentijn Verhallen edited this page Aug 10, 2025 · 2 revisions

Unchained configuration

Unchained v1.2 comes with a bunch of configurations, giving you a head-start in creating good-looking dashboards. You'll find basic configurations present in the *.dist files.

Key Attribute Purpose Options Default
title Unchained application title Unchained
title_icon Icon shown next to the title fa fa-link
theme Light / dark theme auto
locales Supported locales for your installation. ISO-639-1 list of ISO-639 codes [ ]
locale Default / fallback locale one of ISO-639 codes en
dashboard style Main application style default/text, block default
dashboard show_app_stack Show apps nested under categories true/false true
dashboard show_count Show record counter for apps true/false true
dashboard app_block_image Show image within app block
Only for blocky style
true/false false
dashboard show_inactive Show deactivated records when logged out true/false true
navigation style Navigation style default/top, side/offcanvas default
navigation show_home Show home button true/false true
navigation home_icon Home icon (e.g. "⌂ Home") fa fa-home fa fa-home
navigation home_icon_only Only show icon true/false false
navigation show_quicklinks Show quick links; e.g. create record
Only shows "+" now
true/false true

Examples

Default dashboard (light theme) Default styling

Block-style dashboard (light theme) Blocky styling

Default navigation style (top, dark theme) Default nav

Off-canvas navigation style (dark theme) Offcanvas nav

Offcanvas navigation comes with a nice rainbow effect. This is also present on the block layout.

Application registry

Basic application registration finds place in /user/config/applications.yaml. This is the place you define basic configuration, such as routing and visibility. This allows for easy deployment / resignation of apps, without losing your definitions.

This is also the place where you configure your categories (if any). None of the attributes are required and can be autoconfigured.

Attribute Purpose Example
category Assign applications category
New in v1.2
libary
routes Locale based routes {en: books, nl: boeken}
order Display order 3
deactivate Support deactivating records true/false (default: true)

Routing

We can easily update the routes for our applications (and categories). Routes are configured with i18n in mind, so you'll need to define an array of locale routes.

# /user/config/applications.yaml
categories:
  books:
    routes:
      en: books
      de: bucher
      fr: livres

applications:
  authors:
    routes:
      en: authors
      de: autoren
      fr: auteurs

In this case the app authors will resolve to /books/authors, bucher/autoren, etc. If you omit routing configuration, the routes will reflect the application ID.

Application definition

Applications are configured in: /user/config/applications/<app-name>.yaml

Attribute Purpose Required Example Default
category Application category app
Base configuration takes precedence (see Application registry
libary _default
name Alternate application title favorite books (= title.favorite_books) <applicationID> (books = title.books)
label Overrides title. translation string favorite books (= favorite books)
fields Fields configuration X Configuring fields
meta Metadata configuration Meta configuration
sources Configured sources Sources
modules Configure visibility in modules Module Configuration
sort Default record sorting [surname, name, age] / {sort: {age: desc, surname: asc}} Derived from exposed fields
deactivate Support deactivating records <boolean> true

Meta

The meta section allows configuring application metadata, which is otherwise assigned by Unchained. Be mindful that changing these values may change some of the behaviour of the apps.

Attribute Purpose Example Default
exposes Exposed fields for cross-reference display [brand, name, color] The first public field
slug Configure the composition of the slug [surname, name] <exposed>
icon Display icon for textual layout fa fa-film ~
sort Default record sorting [surname, name, age] / {sort: {age: desc, surname: asc}} Derived from exposed fields

Modules

Module configuration applies rules to the modules (dashboard, detail, form):

modules:
  detail:
    # Disable record detail page 
    enabled: false
    # AND/OR
    public: false
  dashboard:
    # Set sorting
    sort: [ surname, name, age ]

Data sorting

Sorting can be configured in a few different places, in order of precedence:

  • application.sort
  • application.meta.sort
  • application.modules.dashboard.sort

If sorting is not configured, it will be derived from the exposed fields.

Deactivating records

In some cases, your records invalidate over time; One of your teammates quits, you get a different coach, an author dies, etc. In that case it's easy to be able to deactivate records, so they're unavailable in forms, but still be present in your database for reference. Records are activated and deactivated for logged-in users.
In case you don't need/want to deactivate records for one or more apps, you can disable the option with deactivate: false in your application configuration. See basic configuration.

Deactivated records are grayed out for logged-in users. The public visibility is set in the Unchained Configuration.

Field definition

Attribute Purpose Application Example
type The type of the field Field types
required Is this field required? (default false) Boolean (true/false) true
public Is this field visible to the public? (default: true) Boolean (true/false) false
default Default value any except source & pointer fruit: {type: choice, options: [apple, orange, banana], default: 1} (= orange, arrays start with 0)
label Overrides translation label any books: {type: number, source: count_books, label: "no of books"} => label.no_of_books
maxlength Maximum length text, number
options Options of a choice field choice, rating [apple, orange, banana]
multiple Allow multiple values? (boolean) choice, source fields true
source Source of the field Converts field to choice, unless specified authors.photo
group_source Group values by another source Applicable to source fields, that can be grouped by a shared foreign key cafe
condition Mark another field as conditional Disables field until other field is set, useful in combination with group_source library
pointer Point to other fields and calculate the value Get date ranges, call extensions Using pointers
_transform Add data transformer Using transformers
dashboard Manage application dashboard representation Visibility levels
ignored Virtual field Useful when value is used as a reference; e.g. pointer field true (default false)
year_min Minimal year in date picker date, datetime "now", 15 (now - 15 years), 1999
year_max Maximum year in date picker date, datetime "now", 15 (now + 15 years), 2150

Boolean attributes are weak typed, meaning you can either assign them as 1, true, yes, on or their falsy counterpart.

Beware when extending options. The values are stored by the index. Adding values in between, will result in shifting values in the frontend. New values must be appended to the back of the list.

Field types

Type SQL equiv default length
text varchar 255
image varchar 255
file varchar 255
url varchar 255
textbox text -
date date -
datetime datetime -
time time -
choice int 11
number int 11
float float 11
boolean tinyint 1
checkbox tinyint 1
rating int 11

Field visibility

Manage field visibility within the dashboard attribute. Only one level can be assigned per field. If a field is assigned to the large breakpoint, and you're viewing in portrait mode, it is hidden from the large view and displayed in the subtable of a row.
You may need to experiment with the ideal field visibility for your (mobile) appliance.

You can also choose to hide a field from the Detail view. This is especially useful for virtual fields (pointers).

Breakpoint Bootstrap counterpart
all all (default)
visible all
invisible never
The field is only displayed in the Detail module
detail alias for invisible
large tablet-l desktop
small mobile-p mobile-l tablet-p
desktop desktop
portrait mobile-p tablet-p
landscape mobile-l tablet-l
mobile mobile-p
tablet tablet-l tablet-p
####
####
field:
  dashboard:
    visibility: mobile
# OR 
field1:
  dashboard: large
  # hide field on detail page
  detail: false

New in v1.2: You can omit the 'visibility' sub-attribute and directly add the setting to dashboard.

Advances configuration

Virtual references

Adding virtual references can sometimes be a good option, when you want to see a foreign reference without saving it in your current app. In the example below shows how this can be used.

The schematic now looks like this: CurrentApp horses_main_id => [Horse] horses_stables_id => [Stables] => [..data..] OR LEFT JOIN horses_stables _s ON _s.id = _h.horses_stables_id

# ...
fields:
  horse:
    source: horses.photo
    required: true
    condition: riding_school
    group_source: stables

  stables:
    source: stables.name # <- defines exact column

sources:
  horses:
    application: horses-main

  stables:
    join_source: horses # <- source alias within this application (see above)
    application: horses-stables # <- source application ID, as configured in "applications.yaml" 
    foreign_column: horses_stables_id # <- column within join_source, per CMS naming convention

Due to the source configuration, the "stables" field is automatically ignored by the database and form module.

Grouping

Grouping choices is a new, long-awaited feature in version 1.2. Before, you were only able to group static values in choice-type fields. Now you can also group them by referencing a common source. This is especially powerful in combination with the conditional form-rule.

We'll expand the example above to demonstrate the configuration.

# applications/horses-main.yaml
application:
  name: Horses

  fields:
    photo:
      type: image
      required: false
    name:
      type: text
      unique: true
      required: true
    stables:
      source: stables.name
      required: true
      dashboard:
        visibility: large
    rides:
      source: count_rides
      dashboard:
        visibility: large

  sources:
    stables:
      application: horses-stables
    count_rides:
      application: horse-riding-log
      function: count
# applications/horse-riding-log.yaml

application:
  name: horse riding log
  title: Riding Log

  fields:
    date:
      type: date
      required: true
    stables: # <- see sources, this is a choice field now
      source: stables # <- common source, used throughout configuration
      required: true
      dashboard:
        visibility: detail
    horse:
      source: horses.photo
      required: true
      condition: stables # <- bind to the field "stables"  
      group_source: stables # <- group by common source alias "stables"
    instructor:
      source: instructors.name
      required: true
      condition: stables # <- bind to the field "stables"
      group_source: stables # <- group by common source alias "stables"
    # ... more

  # no special configuration needed
  sources:
    horses:
      application: horses-main
    instructors:
      application: horses-riding-instructors
    stables:
      application: horses-stables

In this configuration, the "stables" field is used as a regular choice field, so the foreign ID is stored within the application record.

Because this configuration uses both group_source and condition, changing the value of the field "stables" will automatically hide the other groups in the form fields.
If we were to remove the condition attribute, the fields will only be grouped. If we were to keep condition, but remove group_source, the fields won't become active until you select a "stable".

You can also find this configuration in my personal user-config, which I've shared on GitHub.

Pointers

Pointers are special fields, pointing to local fields, calculating their value within an extension. This example uses a custom date range calculation.
The application runtime looks for the extension class in: /user/extensions/app_people.php.

# /user/config/applications/people.yaml
#####
#####
experience:
  type: text
  pointer:
    fields: [ date_started, date_stopped ]
    type: external
<?php
// this example outputs a translatable message`{"time.year.nn", {nn: 2}}`

class People implements \App\System\Constructs\UserExtensionInterface 
{

    use \App\System\Helpers\DateTransformerTrait;
    /**
    * @param array $context ['date_started' => "<yyyy-mm-dd>", 'date_stopped' => "<yyyy-mm-dd>"]
    * @return string  Translation string
    */                       
    public static function transformExperience(array $context): ?\App\System\Constructs\Translatable
    {
        return static::timeAgo($context['date_started'] ?? null, $context['date_stopped'] ?? null);
    }
}

Transformers

Transformers allow fancy transformations like date calculations, number formatting and adding text suffixes. This makes it a lot easier to get the output you want. Transformers are configured per field:

# convert date of birth to age in years
date_of_birth:
  type: date
  label: Age
  required: false
  _transform:
    date:
      round_to: year

# round with precision
average:
  source: avg_score
  type: number
  ignored: true
  public: false
  #dashboard: false
  _transform:
    number:
      round: true # <- true = 'round'
      round_precision: 1

# abbreviate text (Dutch van der Linde => D.v.d.L.)
shorthand:
  type: text
  ignored: true
  pointer:
    fields: [ name, surname ]
  _transform:
    text:
      abbr: true
Field type Transformers Values Effect
date round
round_to
floor, ceil, round
year, month, week, day, hour, minute
Round to whole period, up to present
number round
round_precision
suffix
floor, ceil, round
<int>
<string>
Round number
decimal precision
added suffix
text suffix
abbr
<string>
<boolean>
Add suffix
Abbreviate text
Clone this wiki locally