VoliJS / NestedTypes

BackboneJS compatibility layer for Type-R data framework.
https://volicon.github.io/Type-R/
MIT License
94 stars 17 forks source link

Is there a way to fetch the id for a model referenced via a Store? #155

Closed far-blue closed 7 years ago

far-blue commented 7 years ago

Where I have a model instance that has a reference, by Id, to another model in a Store, is there a way to get hold of the id itself rather than the model from the store? In particular, I am thinking of the following scenario:

We want to create a shared library of models that we use across multiple apps but we neither want all the model definitions loaded into every app or, of course, all the models and all their relationships fetched by every app. If we define an Order model with a reference to a Product model and a reference to a Customer model (as a simple example) then we don't always need to populate both the Product model and the Customer model in order to use the Order model - but we would still want to have access to the Customer Id and Product Id values. If you try to request a Customer when the instance of the Customer model doesn't exist in the store, for instance, you are given back 'undefined' so is there a way to return the Customer Id instead? I believe it is stored in a Refs structure internally?

If we had access to the Id values then we could setup all our models with references and make use of Stores and only populate the store collections we needed and then refer to Id values for those models not populated directly.

gaperton commented 7 years ago

Model.from reference works very simply. It stores an id inside of model.attributes, which is replaced with the model on the first read attempt if the master collection is not empty. Thus, if you want to access the raw id, you could do:

console.log( order.attributes.customer );

// or, to make it 100% correct:

const Order = Model.extends({
    defaults : {
        customer : Model.from( '~customers' ),
        // ...
    },

    properties : {
        customer_id(){
            const { customer } = this.attributes; 
            return typeof customer === 'object' ? customer.id : customer;
        }
    }
}

Or, using the syntax from NestedTypes 2.0 RC:

@define
class Order extends Model {
    static attributes = {
        customer : Model.from( '~customers' ),
        // ...
    }

    get customer_id(){
        const { customer } = this.attributes; 
        return typeof customer === 'object' ? customer.id : customer;
    } 
}
gaperton commented 7 years ago

For the case of the Collection.subsetOf attribute (which represents one-to-many id reference) it's way easier. Just order.customers.getModelIds(). Can't do the same for Model.from as there is no any proxy object there.

Btw, it might be a good idea to put a dummy object instead of the raw id in the attributes. So, even for the unresolved Model.from the construct like order.customers.id will work. Will fix that in v2.0. [UPD - really not sure which kind of problems it might cause, let's put it on hold for a while].

far-blue commented 7 years ago

That's great, thankyou for the examples :) I don't believe the order.customer.id issue won't be a big one because it would be clear from the way the store is initialised whether the customer object exists or not. I'll have to have a play but it all looks very easy. I might even look to automate the provision of Id native property :)

What happens if the master collection is empty and the property is accidentally requested - it appears to be the case that the property is set to 'undefined' so would that mean the Id value is then lost?

gaperton commented 7 years ago

What happens if the master collection is empty and the property is accidentally requested - it appears to be the case that the property is set to 'undefined' so would that mean the Id value is then lost?

That would be just fine. An empty or missing master collection is never used for id resolution. So an id will remain in place in model.attributes, but the property will return null/undefined. Occasionally, it might be resolved.

gaperton commented 7 years ago

It's really the simplest place in the framework. Here you can see what happens. This is an id resolution code in Model.from attribute's getter.

https://github.com/Volicon/Type-R/blob/master/src/relations/from.ts#L61

The reason, why it's resolved on reading, is to allow the mutual references from collections which are loaded independently. So you can wait when all of them are loaded and then render, or render many times in the middle, and it won't break anything.