Mixins
Contents
Mixins are a smart way of dynamically extending existing content types with more fields.
The idea is to basically define new fields and decide what content types those fields should be applied to. If no content type is selected while registering a mixin, those fields will be enabled for all content types.
In Content Studio, a mixin is visually displayed as a separate section of the form, listed below the content type’s standard fields.
| The tasks and examples in this chapter are based on work done in previous chapters (starting with the setup chapter). If you want to follow along with the examples, make sure you’re all caught up. |
Your first mixin
If your requirement is to add new fields to a specific content type, it doesn’t make sense to use a mixin, since you can directly update that content type.
Now, if your requirement is to add new fields to multiple content types, that is when mixins come into place.
The same behaviour of extending multiple content types can be achieved through form fragments. You can create a form fragment and include it in the content types you want to extend. The difference is where the data in those fields is stored, and in the fact that mixins are easier to maintain when adding to a lot of content types - or to content types you don’t own, such as the built-in folders and media types.
|
When to use it
Use a mixin when the fields cut across content types rather than belong to one: SEO settings, editorial notes, tracking data. Mixins reach types you do not own, such as folders and media, can be made optional so editors switch them on per item, and keep their data apart from the type’s own fields under |
Let’s now add a "notes" mixin, which will extend both the Artist and Animal content types.
Similar to content types, mixin schemas have a specific naming and placement convention within your app: src/main/resources/cms/mixins/<mixin-name>/<mixin-name>.yaml.
-
Create the "notes" mixin schema:
src/main/resources/cms/mixins/notes/notes.yamlkind: "Mixin" title: "Notes" form: - type: "TextLine" name: "notes" label: "Notes" occurrences: min: 0 max: 3 -
A mixin schema on its own is dormant. You also need to register it, and define which content types it applies to. This is done in your app’s CMS descriptor,
cms.yaml, which the starter created as an empty file:
kind: "CMS"
mixins:
- name: "notes"
allowContentTypes: "{app-name-full}:{content-type-1}|{app-name-full}:{content-type-2}" (1)
+ <1> A regular expression matched against the full content type name. Leave it out to apply the mixin to every content type in the project.
+ . Your app is redeployed, so open a Artist or an Animal content item. You should see the following section at the bottom of the form:
+ image::notes-x-data.png["Notes mixin fields showing at the bottom of an animal content item",width=1050]
+ . Finally, add some notes to some content.
| For more details and configuration options for mixins, visit the mixins documentation and the CMS descriptor documentation. |
Headless
As you might expect, your mixin fields are also available via the Guillotine API.
Mixin data is stored separately from the content type’s own fields, under the key x, grouped by the app that provides the mixin. Use the following query to quickly access all mixin fields in your content.
{
guillotine {
queryDsl(
query: {
boolean: {
must: [
{
term: {
field: "type",
value: {
string: "com.example.tutorial101:animal"
}
}
},
{
term: {
field: "displayName",
value: {
string: "Lynx"
}
}
}
]
}
}
) {
displayName
xAsJson
}
}
}
If you’ve added any values to your Lynx’s Notes fields, this is what you might get.
{
"data": {
"guillotine": {
"queryDsl": [
{
"displayName": "Lynx",
"xAsJson": {
"com_example_tutorial101": {
"notes": {
"notes": [
"Lynx note 1",
"Lynx note 2",
"Lynx note 3"
]
}
}
}
}
]
}
}
}
Should you require specific fields only, or wish to drill into more details, for instance by following a reference, use the typed GraphQL fields instead of xAsJson. For instance like this:
{
guillotine {
queryDsl(
query: {
boolean: {
must: [
{
term: {
field: "type",
value: {
string: "com.example.tutorial101:animal"
}
}
},
{
term: {
field: "displayName",
value: {
string: "Lynx"
}
}
}
]
}
}
) {
displayName
x {
com_example_tutorial101 { (1)
notes { (2)
notes (3)
}
}
}
}
}
}
| 1 | The app providing the mixin. Dots in the app name become underscores in GraphQL field names. |
| 2 | This is the name of the mixin. In this case, "notes" because we defined it to be like so in the notes.yaml file |
| 3 | This is the "notes" field (TextLine form item) inside the "notes" mixin. |
If you made it here - you are officially equipped with an essential overview of Enonic’s fundamental content modelling capabilities. 🙌
Choosing the right tool
Your content model is complete, so here is everything you have used, side by side. Come back to this table when you model something new.
| Tool | Use it for | Where the data ends up | Sign you picked the wrong one |
|---|---|---|---|
|
Content type |
A kind of item editors create, find and publish on its own |
An item with its own path, status and permissions |
Items that are only ever edited together with one other item |
|
Display name |
The item’s own title |
|
A |
|
Folder |
Grouping items so paths stay meaningful |
The path of every item beneath it |
Paths nobody can predict |
|
Reference (ContentSelector, ImageSelector) |
Pointing at an item that exists on its own |
The target’s ID; the item is reachable through the reference |
Copying the same text or image into many items |
|
Media |
Files that are assets in their own right |
A content item of a media type, with an attachment |
Uploading the same file over and over |
|
AttachmentUploader |
Files that belong to one item |
An attachment on that item |
Wanting to link to the file from elsewhere |
|
FieldSet |
A tidier form |
Nothing; the fields inside are stored as usual |
Expecting it in the API |
|
ItemSet |
Repeating structured data the item owns |
An object, or a list of objects, under |
Needing to publish or reuse the entries separately |
|
OptionSet |
A choice between variants with different fields |
An object with |
Every option is empty; use a ComboBox |
|
Form fragment |
The same fields in several schemas of your app |
Exactly as if the fields were typed in place |
Needing the fields on types you do not own |
|
HtmlArea |
Free-form prose |
One HTML string, with links, images and macros resolved by the API |
Parsing the HTML to find structure |
|
Mixin |
Fields that cut across content types, including built-in ones |
Under |
The fields belong to one type only |
Bonus: SEO Metafields
There’s a well-known app on Enonic Market named SEO Metafields. Its main purpose is to provide SEO capabilities to your project, and for that purpose, it adds a mixin, which is then enabled for all content types in your project.
This is a bonus section, so you’re not missing much if you skip it, but it’s worth installing it to better understand the capabilities of mixins.
-
Install the SEO Metafields app from
XP menu→Applications.
-
Edit your project in Settings and add the app to it.
-
Immediately after adding the app, the form of every content item gets a new section with the SEO fields.
So, now you have some additional fields on all your content. If you check some of your content items, like an animal, you will see the fields are there as well.
Summary
Your content model is complete: content types, sets, references, rich text, media and mixins, all readable through the API.
Next up, let’s fetch it all from a front-end.