在Ember Data中如何在两端保存关系

4

我正在使用带有RestAdapter的Ember Data和以下模型:

Pizzas.Pizza = DS.Model.extend({
  name: DS.attr('string'),
  orders: DS.hasMany('order', { async: true }),
});

Pizzas.Order = DS.Model.extend({
  date: DS.attr('date'),
  pizzas: DS.hasMany('pizza', { async: true }),
});

我创建并保存新订单的步骤如下:

var pizza1 = an existing pizza with id 1;
var pizza2 = an existing pizza with id 2;

var newOrder = this.store.createRecord('order');
newOrder.set('date', Date());

newOrder.get('pizzas').then(function(pizzas) {

    pizzas.pushObject(pizza1);
    pizzas.pushObject(pizza2);
    newOrder.save();
});

这个很好用 - Ember Data 在Order模型上执行POST操作,其中包括在pizzas关系字段中的披萨id。然而,我期望在保存后,Ember Data会自动将订单ID添加到2个披萨对象的orders关系中,但事实并非如此。这会导致两个问题:
  1. 当我查询一个披萨的所有订单时,新订单不会出现(因为它从未添加到披萨的关系字段中)
  2. 当对披萨进行更改(例如名称更改)并保存披萨时,服务器上的新订单关系会丢失(因为它从未添加到披萨的关系字段中,并且PUT仅包括最后从服务器获取的订单ID)

我通过修改上面代码的最后一行解决了这个问题:

newOrder.save().then(function() {

        pizza1.get('orders').then(function(orders) {

            orders.pushObject(newOrder);
        })
        // same for pizza 2
    } );

Ember数据是否需要手动在两端创建关系(就像我现在所做的那样),还是我缺少了什么?

我认为这是预期的行为。你正在定义关系,而不是绝对约束条件。 hasMany() 只是意味着它可以有很多;并不是“在此订单中添加披萨时,订单也必须添加到披萨的订单列表中”。如果框架在幕后执行此操作,那将非常奇怪。 - Prasad Silva
1
@PrasadSilva 我不同意这是可以预料的。我创建了 Ember-Graph 来解决这个确切的问题。如果只在一个端点上更新,那么它不是关系,而只是引用。楼主 - 你使用的 Ember-Data 版本是什么?我认为单一真相来源分支直到 beta 9 或 10 才合并。 - GJK
@GJK 感谢您的回复。我们目前使用的是9版本,但明天将升级到11版本。我们遇到了一些问题,希望这次升级能够解决它们。完成后,我会在这里发布更新。 - RunLoop
从变更日志来看,好像直到 beta 10 才添加了关系变更。你可能需要进行大量的更改才能升级,但你遇到的问题应该会消失。 - GJK
1个回答

2

我正在使用来自自己分支的beta 11加补丁。

持久化关系是您需要自行管理的事情。在Ember Data中,不能有任何硬性或快速规则,因为不同的服务器和json api将具有不同的管理关系方法以及特定的验证和引用完整性规则,这将决定如何管理模型之间的关系。这将为您的模型持久性策略引入排序依赖项。如果您有一个松散的“NoSQL”类型文档服务器,它没有这样的引用完整性要求,那么一开始看起来很容易,最终会出现数据不一致、孤儿、悬空引用等问题。

要小心声称解决您问题的客户端数据持久化层,因为实际上它们只能处理一些狭窄的用例。实际上,这只是在控制器中协调保存的问题,其中知识和上下文属于需要完成的内容。

我发现在json api中有效的策略是仅在具有外键(即“belongsTo”侧)的模型上管理关系,并避免返回或管理hasMany侧的键,因为当您的集合增长时,所有这些ID传递的效率不高。

最好查看JSONSerializer基类的源代码,该类将模型序列化为看看它做了什么,RESTSerializer继承其行为。您将看到,序列化程序只保存ID,并且不会递归级联保存您的对象图,这将是一件坏事,因为您将尝试解决NP-Complete Hamiltonian Path Problem!。这就是为什么我说非常怀疑数据层作出承诺解决您的模型图持久性问题,而是在控制器中协调您知道需要完成的内容。这也与脏跟踪和缓冲更改非常匹配(例如使用带缓冲的代理)。

更新

我忘记添加一些相当明显的东西,但是您可以定义自己的模型序列化程序,可以作为单个POST / PUT事务的一部分侧面保存任何相关模型(而不是使用.then方法),具体取决于您的服务器支持情况。只需覆盖相关的序列化方法即可包含所需内容。如果有一些非常密切相关的模型总是一起创建或更新,则EmbeddedRecordsMixin也可能很有用。

例如,我有一个联系人模型,每个联系人可以有一个或多个名称、地址、电子邮件或网址。而每个模型本身都跟踪偏好/目的,以及有效期从validFrom到validTo等。
首先,我的应用程序序列化器混合了嵌入记录支持和attrs属性的合并,因此我可以继承序列化器:
// app/serializers/application.js
import DS from 'ember-data';

export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin,{
  mergedProperties: [ 'attrs' ]
});

然后我的联系人序列化程序指定了嵌入哪些模型:

// app/serializers/contact.js
import AppSerializer from "./application";

export default AppSerializer.extend({
  attrs: {
    names: { embedded: 'always'},
    addresses: { embedded: 'always' },
    emails: { embedded: 'always' },
    phones: { embedded: 'always' },
    urls: { embedded: 'always' }
  }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接