13

Has anybody come up with an answer for polymorphic associations and ember-data?

We would need some way of being able to query the type at the other end of the relationship from what I can tell.

Anybody any thoughts on this?

dagda1
  • 26,856
  • 59
  • 237
  • 450
  • Can you give a specific example of what you want to do? – pangratz Jul 12 '12 at 18:14
  • I'd like to be able to have something like: JSON: { ... "attachedTo": { "user_id": 16 } ... } and in the model definition: attachedTo: DS.belongsTo("auto") – Jack Casey Sep 27 '12 at 03:37

3 Answers3

8

With the latest ember-data build you can now use polymorphic associations:

You need to configure your Models to make it polymorphic:

/* polymorphic hasMany */
App.User = DS.Model.extend({
 messages: DS.hasMany(App.Message, {polymorphic: true})
});

App.Message = DS.Model.extend({
  created_at: DS.attr('date'),
  user: DS.belongsTo(App.User)
});

App.Post = App.Message.extend({
  title: DS.attr('string')
});

/* polymorphic belongsTo */
App.Comment = App.Message.extend({
  body: DS.attr('string'),
  message: DS.belongsTo(App.Message, {polymorphic: true})
});

You also need to configure alias properties on your RESTAdapter

DS.RESTAdapter.configure('App.Post' {
  alias: 'post'
});
DS.RESTAdapter.configure('App.Comment' {
  alias: 'comment'
});

The result expected from your server should be like this:

{
    user: {
        id: 3,
        // For a polymorphic hasMany
        messages: [
            {id: 1, type: "post"},
            {id: 1, type: "comment"}
        ]
    },

    comment: {
        id: 1,
        // For a polymorphic belongsTo
        message_id: 1,
        message_type: "post"
    }
}

More information in this github thread

Willem de Wit
  • 8,604
  • 9
  • 57
  • 90
  • I've run into an issue when a model has multiple polymorphic associations - a Post can be Commentable, Attachable, and Notifiable - have you run into that issue before? I've partly worked around it by having each of my "polymorphic" models extend from the last but this means I have no control over which attributes appear on each model. – Kevin Ansfield Jun 20 '13 at 09:20
  • Happen to know the reason a `hasMany` replaces an ID with an `{id: 1, type: 'type'}` object, whereas a belongsTo injects this into the parent object? It'd be so much cleaner if they were the same, e.g. `comment: {id: 1, message: {id: 1, type: 'post'}}` – Joe Nov 19 '14 at 20:14
3

So I have something. It's not finished, or entirely clean, but it works. Basically, I use a mixin to bypass the Ember associations entirely. I'm sure that this could be rolled into the adapter or the store, but for now this works.

Polymorphic models come through the the JSON with an itemId and itemType:

App.Follow = DS.Model.extend
  user: DS.belongsTo('App.User')
  itemId: DS.attr("number")
  itemType: DS.attr("string")

I add a mixin to the models that are associated with it :

App.Hashtag = DS.Model.extend App.Polymorphicable,
  follows:(-> 
  name: DS.attr("string")
    @polymorphicFilter(App.Follow, "Hashtag")
  ).property('changeCount')  #changeCount gives us something to bind to

  followers: (->
    @get('follows').map((item)->item.get('user'))
  ).property('follows')

The mixin implements three methods, one that updates the changeCount, one that returns the model's type and the polymorphicFilter method that filters a model by itemType and id:

App.Polymorphicable = Ember.Mixin.create
  changeCount: 1 

  polymorphicFilter: (model, itemType)->
    App.store.filter model, 
      (data) =>
        if data.get('itemId')
          @get('id') is data.get('itemId').toString() and data.get('itemType') is itemType 

  itemType:()->
    @constructor.toString().split('.')[1]

  updatePolymorphicRelationships:()->
    @incrementProperty('changeCount')

The controller layer is protected from all this jankyness, except for having to call updatePolymorphicRelationship to make sure the bindings fire:

App.HashtagController = Ember.ObjectController.extend
  follow:()->
    App.Follow.createRecord({
      user: @get('currentUserController.content')
      itemId: @get('id')
      itemType: @get('content').itemType()
    })
    #this provides a way to bind and update. Could be refactored into a didSave()
    #callback on the polymorphic model.
    @get('content').updatePolymorphicRelationships()
    App.store.commit()

That's what I have so far. I'm trying to keep things in the model layer as it's just one step removed from the adapter layer. If it looks like Ember Data is not going to look at polymorphics at all in future, then it would make sense to pull this all up to a higher level, but for now, this works and leaves my controllers (relatively) clean.

Andre Malan
  • 2,043
  • 12
  • 12
2

Polymorphic associations are now supported in ember data

https://github.com/emberjs/data/commit/e4f7c3707217c6ccc0453deee9ecb34bd65c28b9

Cyril Fluck
  • 1,561
  • 7
  • 9