Open vma-vortx opened 1 year ago
Hi @VictorTBX , thanks for your suggestion.
It seems your suggestion is related to the concept of computed properties, which is a feature that is not yet available in Herbs.
Here are some benchmarks for this feature:
Django: https://docs.djangoproject.com/en/4.1/topics/db/models/#model-methods Laravel: https://laravel.com/docs/8.x/eloquent-mutators#defining-an-accessor Phoenix: https://hexdocs.pm/phoenix/ecto_models.html#computed-fields PostgreSQL: https://www.postgresql.org/docs/current/ddl-generated-columns.html SQL Server: https://learn.microsoft.com/en-us/sql/relational-databases/tables/specify-computed-columns-in-a-table?view=sql-server-ver16 and many others
On vanila JS, you can use getters and setters to achieve this:
const person = {
name: 'Victor',
surname: 'Melias',
get fullname() {
return `${this.name} ${this.surname}`
}
}
With that, my suggestion would be a new parameter in the entity definition, something like:
const Person = entity('Person', {
id: field(Number),
name: field(String),
surname: field(String),
fullname: field(String, { value: (person) => `${person.name} ${person.surname}` })
})
A few things to consider:
Ex:
const Person = entity('Person', {
...
fullname: field(String, { value: (person) => `${person.name} ${person.surname}` })
})
or
const Order = entity('Order', {
...
total: field(Number, { value: (order) => order.items.reduce((total, item) => total + item.price, 0) })
})
const person = new Person({ name: 'Victor', surname: 'Melias' })
console.log(person.fullname) // should return 'Victor Melias'
person.name = 'John'
console.log(person.fullname) // should return 'John Melias'
const person = new Person({ name: 'Victor', surname: 'Melias' })
console.log(person.fullname) // should return 'Victor Melias'
person.fullname = 'John Melias' // should throw an error
const person = new Person({ name: 'Victor', surname: 'Melias' })
console.log(person.fullname) // should return 'Victor Melias'
console.log(person.toJSON()) // should return { id: 1, name: 'Victor', surname: 'Melias', fullname: 'Victor Melias' }
const Person = entity('Person', {
...
fullname: field(String, { value: (person) => `${person.name} ${person.surname}` }),
fullnameLength: field(Number, { value: (person) => person.fullname.length })
})
const Person = entity('Person', {
...
fullname: field(String, { value: async (person) => {
const { name, surname } = await fetchPerson(person.id)
return `${name} ${surname}`
} })
})
This is open to discussion, since this would change how to call a field (sync vs async).
const person = new Person({ name: 'Victor', surname: 'Melias' })
console.log(await person.fullname) // should return 'Victor Melias'
The problem here is that this also changes how to retrive value of a calculated field in glues, like herbs2rest and herbs2gql since they are expecting a sync value.
So having a async value could be a next step, so I think it would be better to have a sync value first.
this
:const Person = entity('Person', {
...
fullname: field(String, { value: function () {
return `${this.name} ${this.surname}`
} })
})
What else do you think we should consider?
Again, thanks for your suggestion. Let's discuss it and see if we can implement it. I think it would be a great feature.
const Person = entity('Person', {
id: field(Number),
name: field(String),
surname: field(String),
fullname: field(String, { value: function () {
if (person.name && person.surname)
return `${this.name} ${this.surname}`
} })
})
const person = new Person({ surname: 'Melias' })
console.log(person.fullname) // should return undefined
person.name = 'Victor'
console.log(person.fullname) // should return 'Victor Melias'
Great, thanks @dalssoft
I think those are all things to be considered and will help to develop this feature. The async I can't see an application for this, since entities should not retrieve information from external sources and all entities methods can be done as sync.
I started developing this feature, but I'm stucked with two problems:
this
:How mandatory are these two concepts?
The async is not developed as well, but I have successfully tested the other features, like toJson()
Great! Thanks @VictorTBX .
I recommend you to create a PR and we can discuss the issues you are facing there. I'm curios to understand why these features are a problem. I think it will be clear with code.
@dalssoft
I've opened a draft https://github.com/herbsjs/gotu/pull/69
Describe the solution you'd like Like in Object-oriented design, I'd like to read a field from an entity based on values of other fields. This would bring a concept of encapsulation to herbs' entities. This property, ideally, should not be able to be modified.
For example:
Additional context In OOD languages this can be done by setting the properties with "private set", but there's no alternative in Herbs to do this without any kind of workaround