Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Virtual Attribute Type RFC #17

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Conversation

Tomaszal
Copy link

Slightly modified my approach from PR #7221 and created this RFC as requested.

RFC Preview Link

@remidej
Copy link
Contributor

remidej commented Aug 17, 2020

I like this idea. Virtual types seem like a great way to make data more readable in the content manager.

Here are some other use cases where it would be useful:

  • Save a user's birthday, but use a virtual field to get his age
  • Save an article's slug, but use a virtual field to get a link to the full URL
  • Generate secret preview links for things like Next.js Preview mode, using the slug or id and a .env token
  • Save a relation between User and Article models, but use a virtual field to count how many articles a user has. It would also be useful to sort or filter by that field.

I'm also curious about how it would fit with the Content Types Builder. We probably won't ask the user to write a function in the CTB, but it still has to know that this field exists.

@Tomaszal
Copy link
Author

Yeah those are some nice use cases, should I add them to the RFC?

Regarding how it would fit in the Content type Builder - I think it should be kept pretty simple. Only ask for a field name with no advanced settings. Perhaps also add a link to the documentation explaining what it is / how to use it.

@alexandrebodin
Copy link
Member

Referencing an article that was initiating prior the the RFC: strapi/strapi#6433

@Tomaszal
Copy link
Author

Tomaszal commented Sep 14, 2020

@alexandrebodin any updates on this?

Copy link
Member

@alexandrebodin alexandrebodin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this proposal. Very happy if we can see virtual fields coming to strapi.

That's a really good start :)

In general I would like you give more details about the content manager (can this be displayed in the list view ? how do we handle sorting, filtering ...) Do we display this field in the edit view ?


+ Either Database Manager plugin (ideally) or both Database Connector Bookshelf & Mongoose plugins should not save virtual attributes to the database
+ Database Manager plugin should execute `virtualAttributes` functions after `find` and `findOne` queries
+ Virtual attribute type should be implemented in Content Type Builder plugin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this to the Content Type builder create a problem: how to add js code to the Model.js file for virtual fields ?

As the content type builder write files it is easy as long as those files are JSON. We could imagine writing the js code in the Admin panel and keeping it in the json and running an eval but I don't really like this.

I think the cleaner approach will be to add a template like system to create virtual fields in the content type builder e.g:

"attributes": {
    "first_name": {
      "type": "string",
    },
    "last_name": {
      "type": "string",
    },
    "full_name": {
      "type": "virtual",
      "template": "${first_name} ${last_name}"
    }
  }

and If you want to you can configure the field programmatically a bit like you showed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't like the idea of running an eval either. The template string sounds like a good idea though. It could be parsed using regular JS template literals by doing something like this. I'll add that to the RFC if this sounds good.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Templates are nice, but would likely limit what calculations can be done.
Can't the calculations be done via lifecycle hooks ?

```js
module.exports = {
virtualAttributes: {
full_name(entity) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we should have getter and setter. This would allow updating database field based on a virtual field.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a nice addition, though it doesn't have a lot of use cases (at least not that I can think of). How would it be best to implement in the model.js file though? Perhaps have two additional objects (getters and setters) in the virtualAttributes object, or modify the name of the virtual attribute function (full_name_get and full_name_set)?

@Tomaszal
Copy link
Author

Tomaszal commented Sep 16, 2020

In general I would like you give more details about the content manager (can this be displayed in the list view ? how do we handle sorting, filtering ...) Do we display this field in the edit view ?

@alexandrebodin I don't really see a reason why this couldn't be displayed, sorted or filtered in the list view. I think it should be handled as regular text in there. Perhaps if the perceived 'type' of the virtual attribute for the content manager is a concern there could be an additional field virtualType. That way whenever content manager sees a virtual attribute it will handle it as it would handle an attribute of virtualType. However, I'm not sure how necessary is this (I haven't looked too deep into the content manager, so you probably know better).

If a setter is added to the virtual attribute functions then yes, this could definitely be displayed in the edit view. However then there is a problem with user being confused if both the original values and the virtual one are displayed. I.e. if user fills in first name and last name, and then fills in the full name, one thing or the other will be overridden.

@darron1217
Copy link

darron1217 commented Oct 22, 2020

How about adding getter and setter on model’s js file?

For example
on api/student/models/student.js

module.exports = {
  virtualAttributes: {
    fullName: {
      // getter
      get(model) {
        return `${model.firstName} ${model.lastName}`
      },
      // setter
      set(model, data) {
        const [firstName, lastName] = data.split(' ')
        model.firstName = firstName
        model.lastName = lastName
      },
      // For filter
      filter(options) {
        if(options.fullName) {
          const [firstName, lastName] = options.fullName.split(' ')
          options.firstName = firstName
          options.lastName = lastName
          delete options.fullName
        }
      },
      // For sort
      sort(options) {
        if(options.fullName) {
          const direction = options.fullName
          options.firstName = direction
          options.lastName = direction
          delete options.fullName
        }
      }
    }
  }
};

@darron1217
Copy link

How about implement it as custom field type?
Then we can create CombinedText type and define getter, setter, filter handler... etc on custom area.
And then just select field type on Admin Page.

@brettwillis
Copy link

@Tomaszal here are some of my comments/inputs:

  • It would be most flexible to allow Promise's/async functions for generating the value incase there needs to be some async operation involved in calculating the value (dependency on another model value, external API, etc.)
  • There is another use-case (detailed below) where the attribute is actually written to the database but is never editable (i.e. the value is generated in the write lifecycle, not the read lifecycle). In my opinion we should allow for such a case. How would this be implemented in your proposal?

Use-case: value generated in write lifecycle

Consider a system that sends push notifications to users when an item is published, and it sets a notifiedAt: Date attribute with the timestamp of the notification (which is stored in the database). The notifiedAt attribute should be visible to the user so they can see if/when a notification was sent, but they can never edit it (i.e. it is "virtual").

This amplifies the need for async operations during calulation (i.e. sending a push notification).

An alternative/simple way of implementing this is just hard-coding such an attribute as readonly in the model's JSON, but it could be integrated into this proposal if it makes sense?

@strapi-cla
Copy link

strapi-cla commented Jan 21, 2021

CLA assistant check
All committers have signed the CLA.

@SvenWesterlaken
Copy link

Any updates on this?

1 similar comment
@Inaztm
Copy link

Inaztm commented Jun 5, 2023

Any updates on this?

@Tomaszal
Copy link
Author

Tomaszal commented Jun 6, 2023

Sorry, I have not used Strapi for any projects since writing this RFC, so I do not have the relevant context to update it with the suggestions from the team. I would suggest someone to take over and update the RFC so that the Strapi team can look into it again.

@SvenWesterlaken
Copy link

No problem @Tomaszal. I was mainly addressing the straps team in order to bring attention to this again as it would ease up a lot of data I would like to add to my models.

@alexandrebodin
Copy link
Member

No problem @Tomaszal. I was mainly addressing the straps team in order to bring attention to this again as it would ease up a lot of data I would like to add to my models.

Hi, this feature does look interesting but isn't something on the roadmap for the time being. Feel free to upvote and comment your specific usecase here https://feedback.strapi.io/feature-requests/p/field-type-computed :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants