RestKit: Creating stubs for foreign key relationships

烂漫一生 提交于 2019-12-22 08:13:04

问题


I am struggling to map the foreign key relationships returned by my JSON API with RestKit.

Specifically, I use Loopback to generate an API with Entities like Team and User. There are two Endpoints by default that return the following JSON:

/Teams/

[
  {
    "title": "Team Title",
    "id": 1,
  }
]

/Users/

[
  {
    "name": "Alice",
    "id": 1,
    "teamId": 1,
  },
  {
    "name": "Bob",
    "id": 2,
    "teamId": 1,
  }, ...
]

Now this is a pretty simple API, right? It should be easy to map with RestKit, but even after reading all those other questions about the topic (e.g. Seeking recommendations for best RestKit/CoreData mapping and JSON structure for shallow routes, Foreign key relationship mapping with RestKit, Restkit: GET remote linked object when foreign key refers to missing local object in Core Data) I just can't figure out how to load the entire object graph into core data.

If it makes things easier, I can alter the API as well, of course. For obvious reasons, I don't want to embed the entire Team object in every User but use a foreign key instead. Also, I want to keep the Endpoints separate, mostly because this is the way Loopback generates the API by default.

So what I tried is calling both Endpoints (in an order that should not matter) with the following mapping:

Team Mapping

RKEntityMapping *teamMapping = [RKEntityMapping mappingForEntityForName:@"Team" inManagedObjectStore:objectManager.managedObjectStore];
[teamMapping addAttributeMappingsFromArray:@[ @"title", @"id" ]];
teamMapping.identificationAttributes = @[ @"id" ];
[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:teamMapping method:RKRequestMethodAny pathPattern:@"Teams" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];

User Mapping

RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:@"User" inManagedObjectStore:objectManager.managedObjectStore];
[userMapping addAttributeMappingsFromArray:@[ @"name", @"id", @"teamId" ]];
userMapping.identificationAttributes = @[ @"id" ];
[userMapping addConnectionForRelationship:@"team" connectedBy:@{ @"teamId": @"id" }];
[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:userMapping method:RKRequestMethodGET pathPattern:@"Users" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];

As suggested in other questions (like the ones linked above), I use an RKConnectionDescription to establish the relationship via a transient teamId property. The mapping works fine with a JSON payload that includes both Team and User objects as arrays (with a keyPath instead of a pathPattern in the response descriptor), but fails with separate Endpoints that I call independantly from each other. In that case, the object the connection relationship refers to may not have been created yet. So to solve this, a Stub Object should be created with only the id property populated in that situation.

How can I achieve this with a RestKit mapping?


Edit: Would it help if I changed the JSON structure from "teamId": 1 to "team": { "id": 1 } and add another response descriptor with the keyPath team? Not sure if that's easy to do with Loopback. What's the best way to do this?


Edit 2: So I added the following stub mapping:

RKEntityMapping *teamStubMapping = [RKEntityMapping mappingForEntityForName:@"Team inManagedObjectStore:objectManager.managedObjectStore];
[teamStubMapping addAttributeMappingsFromDictionary:@{@"teamId": @"id"}];
teamStubMapping.identificationAttributes = @[ @"id" ];
[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:teamStubMapping method:RKRequestMethodAny pathPattern:@"Users" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];

When I turn on trace logging, I don't see this mapping executed, though. What am I doing wrong? Is this mapping skipped because there is another one already matching the same pattern? When I comment out the User mapping, it does its job.


Edit 3: So RestKit does actually use only the last response descriptor added to the object manager of those matching a given path and with equal key paths, as discussed here on GitHub. Thanks to the answer below, using a nil key path mapping solves the issue:

RKEntityMapping *teamStubMapping = [RKEntityMapping mappingForEntityForName:@"Team inManagedObjectStore:objectManager.managedObjectStore];
[teamStubMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@"id"]];
teamStubMapping.identificationAttributes = @[ @"id" ];
[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:teamStubMapping method:RKRequestMethodAny pathPattern:@"Users" keyPath:@"teamId" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];

回答1:


I thought my below answer previously worked, but maybe I always snuck around the issue in the comments about exact matches of response descriptors.

So, create another mapping, but make it a nil keypath mapping and use a keypath on the response descriptor of @"teamId". This will cause RestKit to extract an array of team ids from the response and process them as object items, separate to the user objects.


The JSON is fine. Just create a new response descriptor which uses a teamStubMapping, which is a new mapping like teamMapping but replacing id with teamId. The path pattern of this response descriptor should be @"Users".



来源:https://stackoverflow.com/questions/24701657/restkit-creating-stubs-for-foreign-key-relationships

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!