1. #1
    Sencha User
    Join Date
    Apr 2014
    Posts
    16
    Vote Rating
    3
    araad is on a distinguished road

      2  

    Default Answered: How to achieve the equivalent of hasMany using reference

    Answered: How to achieve the equivalent of hasMany using reference


    Hi,
    I'm having issues creating model associations using "reference" on a field. The association I'm trying to achieve is a one-to-many. The "many" part does not know about the "one" part, since it could be a many for another model.

    For example:
    Company has many Emails
    Person has many Emails

    By using the old way with "hasMany", I would write something like this:
    Code:
            Ext.define('App.model.Email', {
                extend: 'Ext.data.Model',
                idProperty: 'Id'
            });
    
    
            Ext.define('App.model.Company', {
                extend: 'Ext.data.Model',
                idProperty: 'Id',
                 hasMany: [{
                     name: 'Emails',
                     model: 'App.model.Email',
                     associationKey: 'Emails'
                 }],
                proxy: {
                    type: 'rest',
                    url: 'company.json'
                }
            });
    The server sends this as the json response:
    Code:
    {
        "Id": 1,
        "Name": "Abc Inc.",
        "Website": "abc.com",
        "Emails": [{
            "Id": 1,
            "EmailAddress": "sales@abc.com"
        }, {
            "Id": 2,
            "EmailAddress": "support@abc.com"
        }]
    }
    After loading the data, I get a store with Email records that is being referenced in the Company record, which is what I want.

    Now I'm not sure on how to achieve this by replacing the "hasMany" with the new way which is to add a reference to a field.
    Here is my unsuccessful attempt:
    Code:
           Ext.define('App.model.Company', {
                extend: 'Ext.data.Model',
                idProperty: 'Id',
                fields: [{
                    name: 'Emails',
                    reference: {
                        child: 'App.model.Email',
                        associationKey: 'Emails'
                    }
                }],
                proxy: {
                    type: 'rest',
                    url: 'company.json'
                }
            });
    This gives me the Company record but instead of giving me a reference to a store it only gives me the first email as a record.

    Here is a fiddle to demonstrate this:
    https://fiddle.sencha.com/#fiddle/91h

    My understanding is that what I'm doing here sets up a many-to-one association instead of one-to-many. Can anyone tell me if this is wrong, and what is the right way to do this?

    I'm using ExtJS 5.0.1
    Thanks.

  2. The important thing to consider with a "reference" field is that it is a field. In the OP:

    Code:
    Ext.define('App.model.Email', {
        extend: 'Ext.data.Model',
        idProperty: 'Id'
    });
    
    Ext.define('App.model.Company', {
        extend: 'Ext.data.Model',
        idProperty: 'Id',
        hasMany: [{
                     name: 'Emails',
                     model: 'App.model.Email',
                     associationKey: 'Emails'
        }],
        proxy: {
            type: 'rest',
            url: 'company.json'
        }
    });
    The "Email" entity does not have a field that identifies the "Company". In order to use "reference" there needs to be such a field. What Evan suggested was to add one:

    Code:
    Ext.define('Email', {
        extend: 'Ext.data.Model',
        fields: ['id', 'subject', 'content', {
            name: 'companyId',
            reference: 'Company'
        }]
    });
    In the case of a nested load, these Email records will have their "companyId" field set appropriately by the framework (even though the server did not send that field). This may or may not work out in this particular case, depending on whether adding this field is acceptable. If it is not, you'll need to continue to use "hasMany" to declare a "key-less" association.

    In practice, most servers have such "foreign" keys but their API's may not always expose them.

    Does that help clarify things?

  3. #2
    Sencha User
    Join Date
    Apr 2014
    Posts
    16
    Vote Rating
    3
    araad is on a distinguished road

      1  

    Default Bump

    Bump


    Can anyone from the sencha dev team please take a look at this issue or anyone who has any input. Thanks.

  4. #3
    Sencha User
    Join Date
    Jun 2008
    Posts
    7
    Vote Rating
    1
    mahe8116 is on a distinguished road

      1  

    Default


    +1

  5. #4
    Sencha User
    Join Date
    Aug 2014
    Posts
    1
    Vote Rating
    1
    RadioFrequency is on a distinguished road

      1  

    Default


    I am having the same problem, as well, can anyone help out.

  6. #5
    Sencha User
    Join Date
    Aug 2012
    Posts
    87
    Vote Rating
    7
    Answers
    2
    andreas-spindler is on a distinguished road

      0  

    Default


    I'm also struggling with this kind of problem.

  7. #6
    Sencha - Ext JS Dev Team evant's Avatar
    Join Date
    Apr 2007
    Location
    Sydney, Australia
    Posts
    17,055
    Vote Rating
    659
    Answers
    471
    evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute evant has a reputation beyond repute

      1  

    Default


    If you're using references, then the association is generated by the entity that holds the foreign key:

    Code:
    Ext.define('Email', {
        extend: 'Ext.data.Model',
        fields: ['id', 'subject', 'content', {
            name: 'companyId',
            reference: 'Company'
        }]
    });
    
    
    Ext.define('Company', {
        extend: 'Ext.data.Model',
        fields: ['id', 'name']
    });
    Evan Trimboli
    Sencha Developer
    Twitter - @evantrimboli
    Don't be afraid of the source code!

  8. #7
    Sencha User
    Join Date
    Jun 2008
    Posts
    7
    Vote Rating
    1
    mahe8116 is on a distinguished road

      0  

    Default


    Evan, I believe your example has the answer....but I am struggling to see it.......

  9. #8
    Sencha - Ext JS Dev Team dongryphon's Avatar
    Join Date
    Jul 2009
    Location
    Kansas
    Posts
    1,403
    Vote Rating
    148
    Answers
    29
    dongryphon is a splendid one to behold dongryphon is a splendid one to behold dongryphon is a splendid one to behold dongryphon is a splendid one to behold dongryphon is a splendid one to behold dongryphon is a splendid one to behold dongryphon is a splendid one to behold

      1  

    Default


    The important thing to consider with a "reference" field is that it is a field. In the OP:

    Code:
    Ext.define('App.model.Email', {
        extend: 'Ext.data.Model',
        idProperty: 'Id'
    });
    
    Ext.define('App.model.Company', {
        extend: 'Ext.data.Model',
        idProperty: 'Id',
        hasMany: [{
                     name: 'Emails',
                     model: 'App.model.Email',
                     associationKey: 'Emails'
        }],
        proxy: {
            type: 'rest',
            url: 'company.json'
        }
    });
    The "Email" entity does not have a field that identifies the "Company". In order to use "reference" there needs to be such a field. What Evan suggested was to add one:

    Code:
    Ext.define('Email', {
        extend: 'Ext.data.Model',
        fields: ['id', 'subject', 'content', {
            name: 'companyId',
            reference: 'Company'
        }]
    });
    In the case of a nested load, these Email records will have their "companyId" field set appropriately by the framework (even though the server did not send that field). This may or may not work out in this particular case, depending on whether adding this field is acceptable. If it is not, you'll need to continue to use "hasMany" to declare a "key-less" association.

    In practice, most servers have such "foreign" keys but their API's may not always expose them.

    Does that help clarify things?
    Don Griffin
    Engineering Manager - Frameworks (Ext JS / Sencha Touch)

    Check the docs. Learn how to (properly) report a framework issue and a Sencha Cmd issue

    "Use the source, Luke!"

  10. #9
    Sencha User
    Join Date
    Aug 2012
    Posts
    87
    Vote Rating
    7
    Answers
    2
    andreas-spindler is on a distinguished road

      0  

    Default


    But, this also means, if I want to use my E-Mail model by more than one "parent", I have to add a reference for each single "owning" model. E.g. an E-Mail can be part of a "Company", but in my mail client it is also a part of the models "Folder" and "Follow". So, taking your example from above, the E-Mail model would look like this (?):
    Code:
    Ext.define('Email', {
        extend: 'Ext.data.Model',
        fields: ['id', 'subject', 'content', {
            name: 'companyId',
            reference: 'Company'
        },{
            name: 'folderId',
            reference: 'Folder'
        },{
            name: 'followId',
            reference: Follow
        }]
    });
    This means, that the E-Mail model has to know of all the owning models. If I want to do it the other way round, so the "owner models" are the only ones aware of their child-models, there is only the "hasMany" association left?

  11. #10
    Sencha User
    Join Date
    Apr 2014
    Posts
    16
    Vote Rating
    3
    araad is on a distinguished road

      0  

    Default


    In my case, even if I supplied the foreign keys from the server in order to use a reference field instead of hasMany, it would still not work for me.

    Lets say for example that I have many packages, with one common package that is required by all others.

    The common package contains the Email model, the company package contains the Company model and so on. That would mean I would have to require "common" in "company" and vice versa, wouldn't that cause a circular dependency when building?

    Even if, somehow, I don't need to require the other packages in the common one (e.g.: model types can be resolved at run-time) or no circular dependency occurs during the build, that means the common package would still have to be aware of the other packages in order to define each foreign key for all associations.

    Using hasMany works well for my case, but it would be nice to use the reference field in the same way without a child model being aware of its parent models, just like in the case of one-to-one.