-
Couldn't load subscription status.
- Fork 0
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 |
Default dashboard (light theme)

Block-style dashboard (light theme)

Default navigation style (top, dark theme)

Off-canvas navigation style (dark theme)

Offcanvas navigation comes with a nice rainbow effect. This is also present on the block layout.
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) |
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: auteursIn 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.
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 |
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 |
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 ]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.
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.
| 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.
| 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 |
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: falseNew in v1.2: You can omit the 'visibility' sub-attribute and directly add the setting to dashboard.
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 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-stablesIn 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 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 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 |