What you'll learn
  • how to define content models and content model groups via plugins
Can I use this?

This feature is available since v5.11.0.

Overview
anchor

Content models and content model groups can be defined in two ways.

Via the Admin Area
anchor

The most straightforward way to define content models and content model groups would be via the Admin Area application, via the Content Model Editor and the Content Model Groups module. This is especially suitable for users that are not developers, and just want to manage everything in a quick and easy way.

Defining Content Models and Content Model Groups via the Admin Area Application

Using Plugins
anchor

And although we can get pretty far by defining content models and content model groups via the Admin Area, on the other hand, we can also do this within our application code, by registering one or more CmsModelPlugin and CmsGroupPlugin plugins. Once defined, content models and content model groups can be then used via the Admin Area in the same way as if they were created regularly, via the Content Model Editor.

Some of the benefits of this approach are:

  • content models and content model groups get to be stored in version control
  • since everything is done via code, in some case we may receive additional flexibility
  • by default, defined content models and content model groups are available for all available locales
  • basically, only developers have the ability to perform changes on content models and content model groups

In the following sections, we cover a couple of examples that show how to define content models and content model groups via plugins.

Examples
anchor

Basic Example
anchor

In this example, we show how we can define a simple Product content model, that belongs to the E-Commerce content model group. Both plugins can be defined within a single file, in our case, api/code/headlessCMS/src/plugins/models.ts :

api/code/headlessCMS/src/plugins/models.ts
import { CmsModelPlugin } from '@webiny/api-headless-cms/content/plugins/CmsModelPlugin'
import { CmsGroupPlugin } from '@webiny/api-headless-cms/content/plugins/CmsGroupPlugin'

export default [
  // Defines a new "E-Commerce" content models group.
  new CmsGroupPlugin({
    id: 'ecommerce',
    name: 'E-Commerce',
    slug: 'e-commerce',
    icon: 'fas/shopping-cart',
  }),

  // Defines a new "Product" content model.
  new CmsModelPlugin({
    name: 'Product',
    modelId: 'product',
    group: {
      id: 'ecommerce',
      name: 'E-Commerce',
    },
    fields: [
      {
        id: 'name',
        fieldId: 'name',
        type: 'text',
        label: 'Product Name',
        helpText: 'A short product name',
        renderer: { name: 'text-input' },
        validation: [
          {
            name: 'required',
            message: 'Value is required.',
          },
        ],
      },
      {
        id: 'sku',
        fieldId: 'sku',
        type: 'text',
        label: 'SKU',
        placeholderText: 'SKU = Stock Keeping Unit',
        renderer: { name: 'text-input' },
      },
      {
        id: 'price',
        fieldId: 'price',
        type: 'number',
        label: 'Price',
        renderer: { name: 'text-input' },
      },
    ],
    layout: [['name'], ['sku', 'price']],
    titleFieldId: 'name',
  }),
]

Do note that you will also need to import the newly created api/code/headlessCMS/src/plugins/models.ts file and register the created plugins in the api/code/headlessCMS/src/index.ts entrypoint file.

Once registered, we should end up with the following two items in our Admin Area main menu:

Product and E-Commerce Items in the Admin Area Main Menu

As shown in the example, the CmsGroupPlugin receives a CmsContentModel object upon instantiation. It lets us define all of the content model’s properties, like its name, ID (modelId), a content model group to which it belongs to, and most importantly, all of the fields that it consists of.

All of the fields of a single content model are defined via the fields property, which is an array of CmsContentModelField objects. Note that some of the properties that we need to define for each field are simpler than others, for example label or placeholderText. On the other hand, properties like type, renderer.name, and validation.name contain values that actually reference other registered plugins. In case an invalid reference was provided, an error will be thrown and you will have to make corrections.

All available field types (type) , field renderers (renderer.name) , and field validators (validators.name) can be found in our GitHub repository.

Want to learn how to create your own custom field type? Check out the Create a Custom Field Type how-to guide.

Finally, note that both the Product content model and the E-Commerce content model group will be available for all existing locales. If you’re interested in defining a content model only for a specific locale, check out the following example.

Define a Content Model Only for a Specific Locale
anchor

In this example, we show how we can define content models and content model groups only for a specific locale, in our case, the en-US.

api/code/headlessCMS/src/plugins/models.ts
import { CmsModelPlugin } from "@webiny/api-headless-cms/content/plugins/CmsModelPlugin";
import { CmsGroupPlugin } from "@webiny/api-headless-cms/content/plugins/CmsGroupPlugin";
import { ContextPlugin } from "@webiny/handler/plugins/ContextPlugin";
import { CmsContext } from "@webiny/api-headless-cms/types";

export default [
    new ContextPlugin((context: CmsContext) => {
        // Only register needed plugins if the current content locale is set to "en-US".
        if (context.i18nContent.getLocale().code !== "en-US") {
            return;
        }

        context.plugins.register([
            new CmsGroupPlugin({
               ...
            }),
            new CmsModelPlugin({
                ...
            })
        ]);
    })
];

Note that we’ve used the ContextPlugin first, in order to be able to access the dynamic context object, and the context.i18nContent.getLocale method. Once we’ve determined that the en-US is the current locale, we proceed by registering the CmsModelPlugin and CmsGroupPlugin plugins, as seen in the previous example.

In Admin Area, user’s current local is sent with every issued HTTP request, via the x-i18n-locale header.

FAQ
anchor

Can content models (and groups) defined via plugins be edited via Admin Area?
anchor

Content models and content model groups that were defined via plugins cannot be edited via Admin Area (via the Content Model Editor and Content Model Groups module). All of the changes need to be done within the application code.

Are there any differences when it comes to security controls?
anchor

When it comes to security, both ways of defining content models and content model groups have access to the same features and follow the same security-related built-in mechanisms. In other words, via the Security application, you can still decide which users have access to particular content models and content model groups that were defined via plugins, and which don’t.

Can I convert a content model that was defined via Admin Area into a plugin (and vice-versa)?
anchor

You can, but it will require a bit of manual work. For example, if you wanted to convert a content model that was defined via Admin Area into a plugin, you would have to find it directly in the database, and copy the data into your application code and try to fit it into the CmsModelPlugin plugin.

If you’re doing this and require additional assistance, feel free to contact us over our community Slack .

What's the difference between the id and fieldId properties in the CmsModelPlugin plugin?
anchor

Both id and fieldId fields represent unique IDs of the field. We can assign any string value to them, but, for easier maintenance, we suggest you use a camelCase version of the actual name of the field. So, if the name of the field was Author Name, then we’d use authorName as the id and fieldId.

There is a difference in how these two IDs are used internally within Headless CMS’ application code, but this is more important when a content model is defined regularly, via the Content Model Editor. In case where a content model is defined via a plugin, we can simply use the same value for both fields.

What are the values that I can pass to the icon property of the CmsGroupPlugin plugin?
anchor

When defining content model groups via Admin Area, we pick its icon via a simple drop-down menu:

Content Model Groups Form - Icon Selector

On the other hand, when defining content model groups via a plugin, we need to specify the icon manually, by setting the same string value that would be set once an icon was picked from the above seen drop-down menu.

By default, we include three free sets of Font Awesome icons (via the Fort Awesome library): regular , solid , and brands . So, when defining your plugin, simply use the icon code listed on the set’s icons list page, prepended with the set code.

Here are a couple of examples of specifying icons from solid, regular, and brands sets:

  • fas/shopping-cart
  • fas/id-card-alt
  • far/address-book
  • far/copy
  • fab/aws.
  • fab/react.