EmberData: Два модела, свързани с hasMany взаимоотношения

Имам логика на приложение, която изисква два модела да имат реципрочни hasMany връзки. Като пример, представете си набор от проблеми на GitHub, които могат да бъдат маркирани с няколко етикета.

Опитвам се да използвам адаптер, който разширява стандартния RESTAdapter. Цялото приложение работи добре, но двойната връзка hasMany хвърля изключение. Ровейки се в кода, метод inverseBelongsToForHasMany хвърля изключение.

Така че, предполагам, че Ember.Data не поддържа асоциирането на два модела с отношения hasMany от двете страни и всеки hasMany изисква асоциирано принадлежи към. Въпросите ми са:

  1. Това поддържа ли се и проблемът е, че правя нещо нередно?
  2. Ако не се поддържа, планира ли да се появи функция?
  3. Дали това тип асоциация трябва да се избягва в този вид приложения? Ако е така, кой е най-добрият подход или решение?

Благодаря предварително


person escalant3    schedule 08.11.2012    source източник


Отговори (2)


Използваме подобен метод за създаване на обект на асоцииране. Въпреки това, вместо да заменим методите в магазина, ние просто добавихме обектите за присъединяване към API.

така че в моделите, които създаваме:

App.Hashtag = DS.Model.extend({
  hashtagUsers: DS.hasMany('App.HashtagUser', {key: 'hashtag_user_ids'})   
});

App.User = DS.Model.extend({
  hashtagUsers: DS.hasMany('App.HashtagUser', {key: 'hashtag_user_ids'})
});

App.HashtagUser = DS.Model.extend({
  user: DS.belongsTo('App.User'),
  hashtag: DS.belongsTo('App.Hashtag')
});

След това за транзакциите ние просто променяме и ангажираме обекта за присъединяване.

App.UserController = Ember.ObjectController.extend({
  followHashtag: function(tag) {
    var hashtagUser;
    hashtagUser = this.get('hashtagUsers').createRecord({
      hashtag: tag
    });
    tag.get('hashtagUsers').pushObject(hashtagUser);
    App.store.commit();
  }
  unfollowHashtag: function(tag) {
    var itemToRemove;
    itemToRemove = this.get('hashtagUsers').find(function(hashtagUser) {
      if (hashtagUser.get('hashtag') === this) {
        return true;
      }
    }, tag);
    this.get('hashtagUser').removeObject(itemToRemove);
    tag.get('hashtagUser').removeObject(itemToRemove);
    itemToRemove.deleteRecord();
    App.store.commit();   

});

API създава обект HashtagUser и методът follow просто добавя този потребител към двете свързани части.

За премахване изважда асоциираните обекти и унищожава асоциирания обект.

Въпреки че не е толкова елегантно, колкото би могло да бъде, голямата ни мотивация беше, че когато Ember Data се актуализира, трябва да можем да го прехвърлим към проста стандартна поддържана версия на Ember Data по-лесно, отколкото ако сме се забърквали със самия магазин.

person Andre Malan    schedule 09.11.2012
comment
В най-новите версии на Ember Data можете да използвате App.ApplicationSerializer = DS.ActiveModelSerializer.extend({});, за да използвате подчертани външни ключове. - person rxgx; 10.01.2014

Отношенията много към много все още не се поддържат в ember-data. За момента едно възможно заобиколно решение е ръчно управление на таблицата за присъединяване.

A = DS.Model.extend({
  abs: DS.hasMany('Ab'),

  bs: function () {
    return this.get('abs').getEach('b'); 
  }
});

Ab = DS.Model.extend({
  a: DS.belongsTo('A'),
  b: DS.belongsTo('b')
});

B = DS.Model.extend({
  abs: DS.hasMany('Ab'),

  bs: function () {
    return this.get('abs').getEach('a'); 
  }
});

Това е само началната точка. След това трябва да персонализирате вашите модели и адаптер, за да изпращате/получавате/запазвате записи по работещ начин

Например в нашето приложение въвеждаме опция { includedJoin: true } вътре в релациите hasMany и декларираме таблицата за присъединяване като JoinModel

A = DS.Model.extend({
  abs: DS.hasMany('Ab', {includeJoin: true}),
  ...
});

DS.JoinModel = DS.Model.extend();

Ab = DS.JoinModel.extend({
  ... belongsTo relationships ...
});

След това в адаптера заменяме методите за създаване/актуализиране/изтриване, за да игнорираме жизнения цикъл на таблицата за присъединяване в магазина

createRecords: function (store, type, records) {
  if (!DS.JoinModel.detect(type)) {
    this._super(store, type, records);
  }
}

И накрая, в сериализатора, ние отменяме функцията addHasMany, за да изпратим данните за присъединяване към сървъра като вградени идентификатори в родителските модели.

addHasMany: function (hash, record, key, relationship) {
  var 
    options = relationship.options,
    children = [];

  //we only add join models, use of `includeJoin`
  if (options.includedJoin) {
    record.get(relationship.key).forEach(function (child) {
      children.pushObject(child.toJSON({
        includeId: true
      }));
    });
    hash[key] = children;
  }
}

От страна на сървъра ние използваме Rails с ActiveModelSerializer, така че единственото малко сложно персонализиране е, когато когато актуализираме родителските модели, ръчно управляваме релацията на присъединяване и създаваме/изтриваме записи в таблицата за присъединяване.

person sly7_7    schedule 09.11.2012
comment
Благодаря за подробния отговор. Ръчната обработка на връзката беше началната точка, която знаехме, но вашият подробен анализ на промените, които трябва да бъдат направени в адаптера/сериализатора, е невероятен. - person escalant3; 09.11.2012
comment
Добре е да знаеш, че ти помага :). Това не е идеално и очевидно хакове, но изглежда работи за нас. Надявам се, че ще успеете да внедрите сървърната работа с това. - person sly7_7; 09.11.2012