Ember Data: сохранение отношений

Мне нужно сразу сохранить глубокий объект на сервере и не смогли найти примеры в Интернете, в которых используются последние данные ember (1.0.0-beta.4).

Например, с этими моделями: (jsfiddle)

App.Child = DS.Model.extend({
    name: DS.attr('string'),
    age: DS.attr('number'),
    toys: DS.hasMany('toy', {async:true, embedded:'always'}),
});
App.Toy = DS.Model.extend({
    name: DS.attr('string'),
    child: DS.belongsTo('child')
});

И этот код:

actions: {
    save: function(){
        var store = this.get('store'),
            child, toy;

        child = store.createRecord('child', {
            name: 'Herbert'
        });
        toy = store.createRecord('toy', {
            name: 'Kazoo'
        });

        child.set('toys', [toy]);
        child.save();
    }
}  

Он сохраняет JSON только для дочернего объекта, но не для каких-либо игрушек, даже не загруженных:

{
  child: {
    age: null
    name: "Herbert"
  }
}

Нужно ли вручную сохранять игрушки? В любом случае, я могу отправить его на сервер следующего JSON:

{
  child: {
    age: null
    name: "Herbert",
    toys: [{
        name: "Kazoo"
    }]
  }
}

или

{
  child: {
    age: null
    name: "Herbert",
    toys: [1]
  }
}

См. JSFiddle: http://jsfiddle.net/jgillick/LNXyp/2/

Ответ 1

Мне нужен был глубокий объект, а не боковое, поэтому, основываясь на ответе kingpin2k, я придумал следующее:

DS.JSONSerializer.reopen({
    serializeHasMany: function(record, json, relationship) {
        var key = relationship.key,
            property = Ember.get(record, key),
            relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship);

        if (property && relationshipType === 'manyToNone' || relationshipType === 'manyToMany' ||
            relationshipType === 'manyToOne') {

            // Add each serialized nested object
            json[key] = [];
            property.forEach(function(item, index){
                json[key].push(item.serialize());
            });
        }
    }
});

Теперь, когда вы вызываете child.serialize(), он вернет этот объект:

{
  child: {
    name: "Herbert",
    toys: [
      {
        name: 'Kazoo'
      }
    ]
  }
}

Это то, что мне нужно. Здесь jsfiddle с ним в действии: http://jsfiddle.net/jgillick/LNXyp/8/

Ответ 2

Ответы здесь устарели. Ember Data теперь поддерживает встроенные записи, что позволяет делать именно то, что вы хотите сделать, чтобы получить и отправить полный граф объектов в одну большую полезную нагрузку. Например, если ваши модели настроены следующим образом:

App.Child = DS.Model.extend({
    name: DS.attr('string'),
    age: DS.attr('number'),
    toys: DS.hasMany('toy')
});
App.Toy = DS.Model.extend({
    name: DS.attr('string'),
    child: DS.belongsTo('child')
});

Вы можете определить собственный сериализатор для вашей модели Child:

App.ChildSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
  attrs: {
    toys: {embedded: 'always'}
  }
});

Это говорит Ember Data, что вы хотите, чтобы "игрушки" были включены как часть "детской" полезной нагрузки. Ваш ответ HTTP GET из вашего API должен выглядеть следующим образом:

{
  "child": {
    "id": 1,
    "name": "Todd Smith",
    "age": 5,
    "toys": [
      {"id": 1, "name": "boat"},
      {"id": 2, "name": "truck"}
    ]
  }
}

И когда вы сохраните свою модель, Ember Data отправит ее на сервер:

{  
   "child":{  
      "name":"Todd Smith",
      "age":5,
      "toys":[  
         {  
            "id":"1",
            "name":"boat",
            "child":"1"
         },
         {  
            "id":"2",
            "name":"truck",
            "child":"1"
         }
      ]
   }
}

Вот JSBin, который демонстрирует это.

http://emberjs.jsbin.com/cufaxe/3/edit?html,js,output

В JSbin, когда вы нажимаете кнопку "Сохранить", вам необходимо использовать Dev Inspector для просмотра запроса, отправленного на сервер.

Ответ 3

игрушки не могут быть как асинхронными, так и встроенными всегда, это противоречивые варианты. Embedded существует только в активном сериализаторе модели.

toys: DS.hasMany('toy', {embedded:'always'})

игрушки - это отношения ManyToOne, и поскольку отношения существуют на стороне whoTo, более эффективно сохранять отношения во время сохранения игрушки. При этом, если вы создаете его все сразу, то хотите сохранить его в одном большом куске, когда вступает в игру переопределение.

serializeHasMany: function(record, json, relationship) {
  var key = relationship.key;

  var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship);

  if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany' ||
      relationshipType === 'manyToOne') {
    json[key] = get(record, key).mapBy('id');
    // TODO support for polymorphic manyToNone and manyToMany relationships
  }
 },

И ваше сохранение должно быть таким:

    var store = this.get('store'),
        child, toy;

    child = store.createRecord('child', {
        name: 'Herbert'
    });
    toy = store.createRecord('toy', {
        name: 'Kazoo'
    });

    child.get('toys').pushObject(toy);
    child.save().then(function(){
       toy.save();
    },
    function(err){
      alert('error', err);
    });