0

I am trying to set up a polymorphic association for a parent that has multiple different types of children. What I find in the documentation is about the inverse problem: children with different types of parents.

I tried the code below, but that always results in productables of type 'Store', while I want them to be 'Book' or 'DVD'.

class App.Product extends Batman.Model
  @belongsTo 'productable', {polymorphic: true}

class App.Book extends Product
class App.DVD extends Product

class App.Store extends Batman.Model
  @hasMany 'products', {as: 'productable'}

Thanks.

Raymundus
  • 2,173
  • 1
  • 20
  • 35

1 Answers1

0

Yes, you're right. Usually, polymorphism is for a case like this:

class App.Product extends Batman.Model
  @belongsTo 'productable', polymorphic: true

class App.Store extends Batman.Model
  @hasMany 'products', as: 'productable', polymorphic: true

class App.VendingMachine extends Batman.Model
  @hasMany 'products', as: 'productable', polymorphic: true

Now, a Product will store both productable_id and productable_type. productable_type will be either Store or VendingMachine.

But... you want to have children of different types available from a single accessor? I don't know that batman.js supports this out of the box, but here's a way you might be able to get it done:

class App.Store extends Batman.Model
  @hasMany 'products' # setup the relationships individually
  @hasMany 'books'
  @hasMany 'dvds'

  @accessor 'allProducts', -> # then create a custom accessor for all of them
    products = @get('products')
    books = @get('books')
    dvds = @get('dvds')
    products.merge(books, dvds) # returns a new Batman.Set

Actually, the docs say not to use Set::merge inside accessors, but rather to use Batman.SetUnion. If this approach seems like it would work, you might want to look into that!

Then, if you need to send class names back in JSON, you can use @encode on your child models, for example:

class App.Product extends Batman.Model
  @resourceName: 'product'
  @encode 'product_type', 
    encode: (value, key, builtJSON, record) ->   
      builtJSON.product_type = Batman.helpers.camelize(record.constructor.resourceName)

Hope that helps!

Edit:

you could shorthand the relation and accessor, if you think you'll have a lot of them:

class App.Store extends Batman.Model
  @productTypes: [
    'products'
    'books'
    'dvds'
   ]
  for productType in @productTypes
      @hasMany productType

  @accessor 'allProducts', ->
    allProducts = new Batman.Set
    productSets = (@get(pt) for pt in @constructor.productTypes)
    allProducts.merge(productSets...)

Might be worth a shot anyways!

rmosolgo
  • 1,854
  • 1
  • 18
  • 23
  • Thanx for the proper explanation. This works but i'm still trying to figure out some method to handle this if the number of types of children gets bigger and bigger... – Raymundus Jan 26 '14 at 13:11
  • Does your app serve Product, Book and DVD all from one endpoint, or is each class served from its own endpoint? – rmosolgo Jan 27 '14 at 01:45
  • Sorry -- I mean JSON endpoint. Where is the data coming from? Does it request JSON from the server? When batman.js gets JSON, are the Book and DVD records together, or does Book and DVD JSON come separately? – rmosolgo Feb 03 '14 at 23:15
  • I am currently still using Batman.LocalStorage, and will move on to the backend when I get this to work. Does it make any difference? Because then I'll switch to RailsStorage immediately... – Raymundus Feb 04 '14 at 08:58
  • Just curious because I haven't seen JSON for different classes served in the same request! I'm afraid I don't have a better idea, except to shorthand the accessor & relation (editted above) – rmosolgo Feb 06 '14 at 03:22