将本地创建的核心数据对象映射到Web服务器并返回

时间:2016-02-10 00:58:32

标签: ruby-on-rails json core-data mapping

我试图设计一个解决方案,用于跨多个模型将本地创建的嵌套关系映射到服务器并返回。考虑一下我有以下模型:

Order LineItem Payment

Order有很多订单项和付款。

如果我在本地创建订单,将其发送到服务器,我可以映射服务器分配的id属性没问题,因为我正在处理同一个对象。但是,我的困境主要围绕向服务器提交嵌套属性。考虑一下:

我在本地创建了OrderPayment,并将Order

发送到服务器的REST端点

{"email":"test@gmail.com", payments_attributes: [{"amount": 15.50}, {"amount": 12}]}

我收到JSON响应,例如:

{"id": 10, "email":"test@gmail.com", payments_attributes: [{"id": 50, "amount": 15.50}, {"id": 51, "amount": 12}}

将这些嵌套付款id属性映射回正确的Core Data对象的好策略是什么?我显然可以查看支付金额之类的东西,但这很脆弱(想象一下边缘情况!),并且仅适用于支付模式。我需要一个通用的解决方案来用于许多不同的模型。我唯一能想到的是在本地创建UUID,发送它,并让服务器将其与id一起推回,但这需要在服务器端进行大量修补,我讨厌客户管理独特属性的想法。我的后端是Rails,我希望这可以在服务器端进行最小的(如果有的话)更改。

我以前一直在使用RestKit,它神奇地工作(我认为它会破坏本地对象并创建新对象)但我正在努力放弃这种依赖并自己进行映射...以获得性能和控制的原因。

2 个答案:

答案 0 :(得分:0)

听起来你已经知道自己需要做什么了。

使用当前的服务器API,您只能通过查看值来匹配数据。由于它们不能保证是唯一的,因此不能保证匹配它们的结果是可靠的。

如果您向服务器发送一些唯一的ID(返回它),那么您有一个唯一的密钥,可以明确地将您的传出数据与新的传入数据进行匹配。服务器不需要存储此ID或对其执行任何其他操作 - 它只需要确保获取传入的ID并将其与传出的结果数据一起包含。如果多个客户端具有相同的唯一ID,则无关紧要,因为他们所做的只是将传出服务器数据与返回的数据进行匹配。

答案 1 :(得分:0)

这是我提出的解决方案。

服务器修改

  • 将Ruby Mixin添加到将使用此
  • 的所有ActiveRecord::Base
  • 创建可重复使用的基本模板以呈现JSON

密新

module CoreDataIdentification
  extend ActiveSupport::Concern

  included do
    attr_accessor :object_id
  end
end

使用它:

class LineItem < ActiveRecord::Base
  include CoreDataIdentification
end

或简单地(在初始化程序中):

ActiveRecord::Base.send :include, CoreDataIdentification

够简单!这很有效,因为ID不会持久保存到服务器数据库,它只会传回给定的任何内容。

JSON模板(使用RABL gem)

attributes :id
attributes :object_id, if: lambda {|object| object&.object_id }
node(:errors) { |object| object.errors }
node(:created_at) {|object| object.api_timestamp(:created_at) }
node(:deleted_at) {|object| object.api_timestamp(:deleted_at) }
node(:updated_at) {|object| object.api_timestamp(:updated_at) } if locals[:index]
node(:errors) { |object| object.errors } if locals[:errors]

示例order.rabl

object @line_item
extends('base', locals: {index: locals[:index], errors: locals[:errors]})
attributes :order_id, :price, :adjustment_total, :promo_total, :additional_tax_total, :included_tax_total, :additional_service_fee_total, :included_service_fee_total, :quantity, :variant_id, :currency, :pre_tax_amount

客户端代码(Objective-C)

  • 子类NSManagedObject,用作管理所有模型(idobject_idcreated_at等)之间共同属性的模板
  • 方法:将核心数据属性序列化为JSON
  • 方法:将JSON属性映射到Core Data
  • 方法:将JSON关系映射到Core Data

我还在玩的一些示例代码:

- (NSMutableDictionary *)requestParams
{
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    if (self.id)
        params[@"id"] = self.id;
    else {

        params[@"object_id"] = self.objectID.URIRepresentation.absoluteString;

    }

    return params;
}

并绘制关系:

- (void)mapRelationships:(NSDictionary *)JSON
{
    [self connectKeyPath:@"line_items" dict:JSON entity:@"LineItem"];
    [self connectKeyPath:@"payments" dict:JSON entity:@"Payment"];
    //[self connectKeyPath:@"adjustments" dict:JSON entity:@"Adjustment"];

}



+ (GMManagedObject *)objectFromURL:(NSString *)URL inContext:(NSManagedObjectContext *)context
{
    NSURL *url = [NSURL URLWithString:URL];
    NSManagedObjectID *objectID = [[GListManager shared].managedObjectStore.persistentStoreCoordinator managedObjectIDForURIRepresentation:url];
    return [context objectWithID:objectID];
}

- (void)connectKeyPath:(NSString *)keyPath dict:(NSDictionary *)JSON entity:(NSString *)entityName
{
    NSArray *items = [JSON objectForKey:keyPath];

    for (NSDictionary *item_attributes in items) {

        NSString *object_id = [item_attributes objectForKey:@"object_id"];
        NSNumber *id = [item_attributes objectForKey:@"id"];

        GMManagedObject *item;

        if (object_id) {
            // find local
            item = [self.class objectFromURL:object_id inContext:self.managedObjectContext];
        } else {
            item = [self findOrCreateByEntity:entityName andId:id];
        }

        [item mapResponse:item_attributes];

    }
}

我仍然需要做很多工作来重构灵活性和性能......但我认为这是向正确方向迈出的一步......渲染数千行RestKit代码没有实际意义。