4

相互のhasMany関係を持つために2つのモデルを必要とするアプリケーションロジックがあります。例として、いくつかのラベルでタグ付けできる一連のGitHubの問題を想像してみてください。

デフォルトのRESTAdapterを拡張するアダプターを使用しようとしています。すべてのアプリケーションは正常に動作しますが、二重のhasMany関係は例外をスローします。コードを掘り下げると、メソッドinverseBelongsToForHasManyが例外をスローします。

したがって、Ember.Dataは、両側のhasMany関係を持つ2つのモデルの関連付けをサポートしておらず、すべてのhasManyには関連付けられたbelongsToが必要です。私の質問は次のとおりです。

  1. これはサポートされており、問題は私が何か間違ったことをしているだけですか?
  2. サポートされていない場合、表示される予定の機能ですか?
  3. これは、この種のアプリケーションで避けるべき関連付けタイプですか?もしそうなら、どちらが最善のアプローチまたは回避策ですか?

前もって感謝します

4

2 に答える 2

8

多対多の関係は、ember-dataではまだサポートされていません。現時点では、考えられる回避策の1つは、結合テーブルを手動で管理することです。

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 ...
});

次に、アダプタで、ストア内の結合テーブルのライフサイクルを無視するために、create / update/deleteメソッドをオーバーライドします

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

addHasMany最後に、シリアライザーでは、結合データを親モデルの埋め込みIDとしてサーバーに送信するために、関数をオーバーライドします。

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;
  }
}

サーバー側では、ActiveModelSerializerでRailsを使用しているため、親モデルを更新するときに、結合関係を手動で管理し、結合テーブルのエントリを作成/削除する場合に、少しトリッキーなカスタマイズを行うだけです。

于 2012-11-09T08:16:37.107 に答える
8

アソシエーションオブジェクトを作成する同様の方法を使用します。ただし、ストア内のメソッドをオーバーライドする代わりに、結合オブジェクトを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が更新されたときに、ストア自体をいじった場合よりも簡単に、EmberDataがサポートする単純なストックバージョンに移行できるようにすることでした。

于 2012-11-09T20:58:57.963 に答える