webiny-js icon indicating copy to clipboard operation
webiny-js copied to clipboard

Enhanced naming functionality for entities in headlessCMS

Open bpietravalle opened this issue 3 years ago • 2 comments

Is your feature request related to a problem? Please describe.

Hi there,

titleFieldId allows us to define entities based on a single property. It'd be useful to enable entities to be named by custom logic that is calculated on save.

For instance, it'd help to name the entity below boom.com - My Industry image

Thanks for your time

Describe the solution you'd like.

  1. Define a template eg, ${myVar1} - ${myVar2}
  2. Define a custom function, stored in api/code, that takes the current entity as an arg

Describe alternatives you've considered.

The only alternative I see is to have a property on the entity specifically for its display name. This is pretty common, I know. But I've found it challenging to rely on content writers to adhere to naming conventions (my current "CMS" in google docs would make u cry). The best solution would separate naming from the users of the admin ui

bpietravalle avatar Dec 01 '21 13:12 bpietravalle

This issue is stale because it was opened 120 days with no activity. Remove the "stale-issue" label or leave a comment to revive the issue. Otherwise, it will be closed in 7 days.

webiny-bot avatar Apr 16 '22 07:04 webiny-bot

This issue is stale because it was opened 120 days with no activity. Remove the "stale-issue" label or leave a comment to revive the issue. Otherwise, it will be closed in 7 days.

webiny-bot avatar Aug 15 '22 07:08 webiny-bot

@bpietravalle - I had a similar(ish) use case, where I wanted to calculate field values on insert/update. I took the approach of creating a GraphQL API plugin that hooks in to the headless CMS entry lifecycle.

import {
    OnEntryBeforeCreateTopicParams,
    OnEntryBeforeUpdateTopicParams
} from "@webiny/api-headless-cms/types";
import { PbContext } from "@webiny/api-page-builder/types";
import { ContextPlugin } from "@webiny/handler-aws";

const contextPlugin = new ContextPlugin<PbContext>(async context => {
    const cb = async ({
        model,
        entry
    }: OnEntryBeforeUpdateTopicParams | OnEntryBeforeCreateTopicParams) => {
        // If the model is tagged to auto-calculate the title field
        if (model.tags?.find((value) => value === "calculate:title")) {
            const titleFieldId = model.titleFieldId;
            const titleValue = /* Formatting logic goes here... */;

            // Apply any additional validation such as a regex, uniqueness (below), etc.
            const manager = await context.cms.getEntryManager(model);
            const [existing] = await manager.listLatest({
                where: {
                    [titleFieldId]: titleValue
                },
                limit: 1
            });

            if (existing.length > 0) {
                throw new Error(`Title of "${titleValue}" already exists.`);
            }

            // Override any previously-store or user-supplied value with the calculated value
            entry.values[titleFieldId] = titleValue;
        }
    };

    context.cms.onEntryBeforeCreate.subscribe(cb);
    context.cms.onEntryBeforeUpdate.subscribe(cb);
});

export default contextPlugin;

You could make this even more dynamic. For your use case, I think you could create tags on the model that represent format strings and call sprintf from within the plugin. But that does open up a number of potential injection opportunities.

Hopefully this gets you started.

owenfarrell avatar Oct 22 '23 13:10 owenfarrell

@owenfarrell Thanks for sharing the example! Tags are a cool way to enable functionality dynamically, that's what we are doing ourselves in different scenarios, and not only with Headless CMS :)

It would be great to have this in our docs @swapnilmmane.

Pavel910 avatar Oct 22 '23 13:10 Pavel910