Search code examples
ember.jsember-data

Ember data - use property other than id for hasMany relationship


I have a many-to-many relationship defined between my tag and payment models as shown below.

//models/tag.js
import Model, { attr, hasMany } from '@ember-data/model';

export default Model.extend({
  name: attr('string'),
  backgroundColour: attr('string'),
  textColour: attr('string'),
  payments: hasMany('payment')
});

// models/payment.js
import Model, { attr, hasMany } from '@ember-data/model';

export default Model.extend({
  date: attr('date'),
  amount: attr('number'),
  paymentId: attr('string'),
  tags: hasMany('tag'),
});

By default, when I add tags to payments, the id of the payment is used as the key for the relationship. My aim is for Ember data to use the paymentId property as the key for this relationship instead.

The snippet below shows the structure of the data I'm loading, where a tag references payments by the paymentId property.

    // Example tag
    {
      "id": "25",
      "name": "Groceries",
      "backgroundColour": "31b04b",
      "textColour": "ffffff",
      "payments": ["20190121201902210"] // References paymentId rather than id
    },
    
    // Example payment
    {
      "id": "1"
      "date": "2019-01-27T22:00:00.000Z",
      "amount": 1644.44,
      "paymentId": "20190121201902210",
      "tags": ["25"]
    }

I've tried to customise the payment serializer as below,

// serializers/payment.js

import ApplicationSerializer from './application';

export default ApplicationSerializer.extend({
  keyForRelationship(key, _relationship) {
    if (key === 'payments') {
      return 'paymentId';
    }
  },
});

However, when the models are loaded I get this error: Assertion Failed: All elements of a hasMany relationship must be instances of Model, you passed [ "20190121201902210" ].

How can I make Ember data use paymentId rather than id when looking up related payments?


Solution

  • I assume that you are using RESTSerializer. It has a primaryKey option, which should be used if the primary key is not named id in your API payload.

    It seems to be a little bit difficult for your example as that record seems to have two primary keys: id and paymentId. If only one of them is used to reference related records I would recommend to simply ignore the other one.

    If both are used to reference related records, you are in a bad situation. Maybe you can change the API?

    If that's not possible I guess you need to map one ID to the other in a serializer which requires to have the payment record loaded before. That gets tricky as serializers are sync which means the record must be loaded before. I guess you will face a lot of edge cases until such a solution is stable - and breaking it would be quiet easy as it highly depends on timing.

    So maybe you should even consider not using Ember Data for these resources at all. It highly depends on the assumption that each resource could be identified by a combination of it's type and ID. But it sounds like for you the same resource could be identified by two different IDs.