Skip to main content

Custom data types

In WordPress there are many solutions for storing specific data in your CMS.
As a core feature WordPress provides custom post types. That is very convenient if you want to manage in the CMS other basic types of content: products, movies or whatever. And if you want to extend existing posts with some custom fields, you can install a plugin to handle the data.

In Cromwell CMS these concepts are implemented in the core. In design this is a compilation of three well-known features:

Custom fields#

Let's say you want to add a new field for products called "Brand". In the admin panel go to the Settings -> Custom data -> Product and click plus button in the Product section. This will create a custom field.

A field has several properties:

  • Key. The key will be used in GraphQL queries to retrieve your data. Note that key must be unique for one entity.
  • Label will be displayed in the admin panel edit page.
  • Field type. You can choose one of many field types:
    • Simple text. Just a text string.
    • Text editor. Rich text editor that used to write posts.
    • Select. User will be able to pick a defined value
    • Image. Will store URL to an image. It can be one from CMS media or any external source.
    • Gallery. Several images in one field.
    • Color. Uses admin panel Colorpicker to choose a color.

Type "Brand" in Key and in Label and hit "SAVE" button at the top. Now go Store -> Products -> Edit a product and you will notice a new field called "Brand" at the bottom of the page. If you want to see "Brand" column in the product list, go back to the list and hit gear icon at the top right. Click a checkbox against Brand and close the popup by clicking anywhere on the page. Column Brands will appear in the list. Now you can search or sort all products by this new column.

How this works#

Cromwell CMS has meta data tables to store any key-value pairs. Data of custom fields stored in this meta data. After user fills any text into your custom field, CMS will create a record in meta table with entity id, key and value. After that we can search or sort over meta data using SQL joins and TypeORM query builder.
The concept is similar to WordPres but there's a difference. WordPress has one table for all metadata called wp_postmeta. On huge websites it can vastly bloat which will slow down queries. That's why in Cromwell CMS we have a meta table per default entity. Products will store meta in crw_product_meta, posts in crw_post_meta.

Custom entities#

Custom entities are similar to custom post types in WordPress. For example, you want to store movies in your CMS. In the admin panel go to Settings -> Custom data, scroll to the bottom and click "Add entity" button. This will open popup with several properties:

  • Type. Custom entity type/name. Will be used in GraphQL queries. Must be unique. (Eg. Movie)
  • List label. How to call many entities, will be used in the admin sidebar as a link title. (Eg. Movies)
  • Entity label. How to call one entity. (Eg. Movie)
  • Icon. Icon to display for the admin sidebar link.

Click apply, it will create a new entity type. After that you can add custom fields to this entity. Click save and Movies link should appear in the sidebar under Blog link.

Same as with meta we don't want to bloat default tables, so custom entities are stored in their own table called crw_custom_entity

GraphQL#

You can query fields you need at the frontend with GraphQL. But there is a small inconvenience that we cannot dynamically change GraphQL types to include custom fields, so all fields are requested in GraphQL argument:

query GetPostById {  getPostById(id: 15) {    title    customMeta(keys: ["key1", "key2"])  }}

If the post has any meta data corresponding to provided keys it will return the following object:

{  "data": {    "getPostById": {      "title": "My post",      "customMeta": {          "key1": "key 1 value",          "key2": "key 2 value"      }    }  }}

customMeta will have null value if all requested keys aren't set.

And here's a basic example how to make a custom query to CMS API server in your code:

import { gql } from '@apollo/client';import { getGraphQLClient } from '@cromwell/core-frontend';
const client = getGraphQLClient();const result = await client.query({  query: gql`    query GetPostById {      getPostById(id: 15) {        title        customMeta(keys: ["key1", "key2"])      }    }  `,});const post = result.data?.getPostById;

Custom entities should be requested via filter method:

query GetFilteredCustomEntities {  getFilteredCustomEntities(    filterParams: {      entityType: "Brand"    },     pagedParams: {      pageNumber: 1,      pageSize: 15    }) {    elements {      id      slug      entityType      customMeta(keys: ["key1", "key2"])    }    pagedMeta {      pageNumber      pageSize      totalElements      totalPages    }  }}

Sorting & filtering#

Cromwell CMS has a base filter that allows to sort or filter over any meta key or entity default column. For example, you want to get only entities with key1 that contains text1 in value (SQL LIKE operator) and sort by key2:

query GetFilteredCustomEntities {  getFilteredCustomEntities(    filterParams: {      entityType: "Brand",      filters: [        {          key: "key1",          value: "text1",          inMeta: true        }      ]      sorts: [        {          key: "key2",          inMeta: true        }      ]    },     pagedParams: {      pageNumber: 1,      pageSize: 15    }) {    elements {      id      slug      entityType      customMeta(keys: ["key1", "key2"])    }  }}

Note that we have inMeta: true to indicate that we want to work with data in meta. If inMeta: false or not specified, then CMS will consider key as a column name in entity table. For example, if you want to filter by post title, then provide key: "title".

Register custom entity or field in the code#

If in your plugin you want to add a new custom entity there two options (look into Plugin development guide to understand where to put this code):

  • Register dynamically on the frontend, without saving into DB. Note that user won't be able to delete it.
src/admin/index.tsx
import { registerCustomEntity, registerCustomFieldOfType } from '@cromwell/admin-panel';
registerCustomEntity({  entityType: 'my_entity_name',  listLabel: 'My entities',  entityLabel: 'My entity',  columns: [    {      name: 'my_field',      label: 'My field',      type: 'Simple text'    }  ]});
registerCustomFieldOfType({    entityType: 'my_entity_name';    fieldType: 'Simple text',    key: 'my_field2',    id: 'my_entity_name-my_field2',    label: 'My Field 2', // Label on entity edit page    column: {      name: 'my_field2',      label: 'My Field 2' // Column name on entity table page    }})
  • Create and save into database on plugin install. User will be able to modify and delete it.
src/backend/index.ts
import { registerAction } from '@cromwell/core-backend';import { getRestApiClient } from '@cromwell/core-frontend';
const client = getGraphQLClient();
registerAction({  pluginName: 'your-plugin-name',  actionName: 'install_plugin',  action: async (payload) => {    if (payload.pluginName === 'your-plugin-name') {      const settings = await client.getAdminCmsSettings();      if (!settings.customEntities) settings.customEntities = [];      if (!settings.customFields) settings.customFields = [];            settings.customEntities.push({          entityType: 'my_entity_name',          listLabel: 'My entities',          entityLabel: 'My entity',      });      settings.customFields.push({        /* ... */      })      await client.saveCmsSettings(settings);    }  }});