我试图设计一个解决方案,用于跨多个模型将本地创建的嵌套关系映射到服务器并返回。考虑一下我有以下模型:
Order
LineItem
Payment
Order
有很多订单项和付款。
如果我在本地创建订单,将其发送到服务器,我可以映射服务器分配的id
属性没问题,因为我正在处理同一个对象。但是,我的困境主要围绕向服务器提交嵌套属性。考虑一下:
我在本地创建了Order
和Payment
,并将Order
{"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,它神奇地工作(我认为它会破坏本地对象并创建新对象)但我正在努力放弃这种依赖并自己进行映射...以获得性能和控制的原因。
答案 0 :(得分:0)
听起来你已经知道自己需要做什么了。
使用当前的服务器API,您只能通过查看值来匹配数据。由于它们不能保证是唯一的,因此不能保证匹配它们的结果是可靠的。
如果您向服务器发送一些唯一的ID(返回它),那么您有一个唯一的密钥,可以明确地将您的传出数据与新的传入数据进行匹配。服务器不需要存储此ID或对其执行任何其他操作 - 它只需要确保获取传入的ID并将其与传出的结果数据一起包含。如果多个客户端具有相同的唯一ID,则无关紧要,因为他们所做的只是将传出服务器数据与返回的数据进行匹配。
答案 1 :(得分:0)
这是我提出的解决方案。
服务器修改
ActiveRecord::Base
类
密新
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)
id
,object_id
,created_at
等)之间共同属性的模板我还在玩的一些示例代码:
- (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代码没有实际意义。