4

ユーザーとグループをRestKitを介してCoreDataオブジェクトにマップし、2つの間の関係を維持しようとしています。

ユーザーの JSON は次のようなものです

{"users":
  [{"fullname": "John Doe", "_id": "1"}
   ...
  ]
}

グループのJSONは次のようなものです

{"groups":
  [{"name": "John's group", "_id": "a",
    "members": ["1", "2"]
   ...
  ]
}

また、対応する Core Data オブジェクト と もUserありGroup、グループとユーザーの間に 1 対多の関係がマッピングされています。の関係プロパティGroupが呼び出されmembers、別のNSArray呼び出されたプロパティmemberIDsがすべてのメンバー ユーザーの文字列 ID の配列を保持します。

RestKit で達成したいことは、これらのオブジェクトをロードし、関係をマップすることです。ユーザーをロードするためのコードは単純な標準的なものであり、グループをロードするためのコードは次のようなものです

RKObjectManager* objectManager = [RKObjectManager sharedManager];
RKManagedObjectMapping* groupMapping = [RKManagedObjectMapping mappingForClass:[Group class] inManagedObjectStore:objectManager.objectStore];
groupMapping.primaryKeyAttribute = @"identifier";
[groupMapping mapKeyPath:@"_id" toAttribute:@"identifier"];
[groupMapping mapKeyPath:@"name" toAttribute:@"name"];
[groupMapping mapKeyPath:@"members" toAttribute:@"memberIDs"];
[objectManager.mappingProvider setMapping:groupMapping forKeyPath:@"groups"];

// Create an empty user mapping to handle the relationship mapping
RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class] inManagedObjectStore:objectManager.objectStore];

[groupMapping hasMany:@"members" withMapping:userMapping];
[groupMapping connectRelationship:@"members" withObjectForPrimaryKeyAttribute:@"memberIDs"];

RKObjectRouter* router = objectManager.router;

[router routeClass:[Group class] toResourcePath:@"/rest/groups/:identifier"];
[router routeClass:[Group class] toResourcePath:@"/rest/groups/" forMethod:RKRequestMethodPOST];
// Assume url is properly defined to point to the right path...
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader) {}];

このコードを実行すると、次の警告が数回吐き出されます (関係ごとに約 2 回のようです)。

2012-10-24 08:55:10.170 Test[35023:18503] W restkit.core_data.cache:RKEntityByAttributeCache.m:205 Unable to add object with nil value for attribute 'identifier': <User: 0x86f7fc0> (entity: User; id: 0x8652400 <x-coredata:///User/t01A9EDBD-A523-4806-AFC2-9B06873D764E531> ; data: {
    fullname = nil;
    identifier = nil;
})

奇妙なことは、関係が適切に設定されていることです(sqliteデータベースを見ても、すべてがうまく見えます)が、RestKitコードで明らかに問題が発生していることに満足できず、何か関係があるようです.上記のコードで作成した偽のユーザー マッピング (ID の配列にマップするものが何もないため、空です)。

たとえば、キーパスマッピングを追加するときなど、代替手段を試しました。

[userMapping mapKeyPath:@"" toAttribute:@"identifier"];

キーパスが空であるため、明らかに文句を言います

2012-10-24 09:24:11.735 Test[35399:14003] !! Uncaught exception !!
[<__NSCFString 0xa57f460> valueForUndefinedKey:]: this class is not key value coding-compliant for the key .

そして、実際のUserマッピングを使用しようとすると

[groupMapping hasMany:@"members" withMapping:[[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"users"]];

次のエラーが表示されます

2012-10-24 09:52:49.973 Test[35799:18403] !! Uncaught exception !!
[<__NSCFString 0xa56e9a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key _id.
2012-10-24 09:52:49.973 Test[35799:18403] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0xa56e9a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key _id.'

RestKit は memberIDs 配列内の ID 文字列自体をUserオブジェクトにマップしようとしているようですが、これは意味がありません。idリレーションシップ配列が、他のオブジェクトへの参照 ID を持つキー (たとえば と呼ばれる) を持つ辞書のリストである例を見てきました。次に例を示します。

{"groups":
  [{"name": "John's group", "_id": "a",
    "members": [
      {"_id": "1"},
      {"_id": "2"}
     ]
   ...
  ]
}

しかし、JSON をそのように変更したくありません (さらに、RestKit が他の方法をサポートするのに十分柔軟であることを望んでいます)。

RestKit でこの種の関係マッピングを行う適切な方法を知っている人はいますか?

アップデート

最後に、REST インターフェイスを変更して、ユーザー オブジェクト ID を含む辞書のリストを送信し (上記の最後の例と同じように)、それを機能させました。まだセットアップに完全に満足していませんが、コードは次のようになります

RKObjectManager* objectManager = [RKObjectManager sharedManager];
RKManagedObjectMapping* groupMapping = [RKManagedObjectMapping mappingForClass:[Group class] inManagedObjectStore:objectManager.objectStore];
groupMapping.primaryKeyAttribute = @"identifier";
[groupMapping mapKeyPath:@"_id" toAttribute:@"identifier"];
[groupMapping mapKeyPath:@"name" toAttribute:@"name"];
[groupMapping mapKeyPath:@"members._id" toAttribute:@"memberIDs"];
[objectManager.mappingProvider setMapping:groupMapping forKeyPath:@"groups"];

// Create an empty user mapping to handle the relationship mapping
RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class] inManagedObjectStore:objectManager.objectStore];
userMapping.primaryKeyAttribute = @"identifier";
[userMapping mapKeyPath:@"_id" toAttribute:@"identifier"];

[groupMapping hasMany:@"members" withMapping:userMapping];
[groupMapping connectRelationship:@"members" withObjectForPrimaryKeyAttribute:@"memberIDs"];

RKObjectRouter* router = objectManager.router;

[router routeClass:[Group class] toResourcePath:@"/rest/groups/:identifier"];
[router routeClass:[Group class] toResourcePath:@"/rest/groups/" forMethod:RKRequestMethodPOST];
// Assume url is properly defined to point to the right path...
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader) {}];

念のため、これが同様の状況にある他の人に役立ちます。私が取ったアプローチにはまだいくつかの問題があるかもしれませんが、これまでのところうまくいっています (順不同の読み込みも処理できます。これは素晴らしいことです)。

4

1 に答える 1

2

これを機能させる方法を見つけました。また、私の質問の更新されたアプローチは、参照オブジェクトではなく、ネストされたオブジェクトにのみ使用されることになっていることに気付きました。このスレッドは私を正しい軌道に乗せました: https://groups.google.com/forum/#!msg/restkit/swB1Akv2lT ​​E/mnP2OMSqElwJ (RestKit で関係を扱っている場合は読む価値があります)

グループの JSON がまだ元の JSON のように見えるとします。

{"groups":
  [{"name": "John's group", "_id": "a",
    "members": ["1", "2"]
   ...
  ]
}

また、ユーザーがキーパスにマップされていると仮定しますusers(つまり、 のようなもの[objectManager.mappingProvider setMapping:userMapping forKeyPath:@"users"];)。関係をマップする正しい方法は次のようになります。

RKObjectManager* objectManager = [RKObjectManager sharedManager];
RKManagedObjectMapping* groupMapping = [RKManagedObjectMapping mappingForClass:[Group class] inManagedObjectStore:objectManager.objectStore];
groupMapping.primaryKeyAttribute = @"identifier";
[groupMapping mapKeyPath:@"_id" toAttribute:@"identifier"];
[groupMapping mapKeyPath:@"name" toAttribute:@"name"];
[groupMapping mapKeyPath:@"members" toAttribute:@"memberIDs"];
[objectManager.mappingProvider setMapping:groupMapping forKeyPath:@"groups"];

[groupMapping mapKeyPath:@"users" toRelationship:@"members" withMapping:[[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"users"] serialize:NO];
[groupMapping connectRelationship:@"members" withObjectForPrimaryKeyAttribute:@"memberIDs"];

RKObjectRouter* router = objectManager.router;
[router routeClass:[Group class] toResourcePath:@"/rest/groups/:identifier"];
[router routeClass:[Group class] toResourcePath:@"/rest/groups/" forMethod:RKRequestMethodPOST];
// Assume url is properly defined to point to the right path...
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader) {}];

以前にこのアプローチを試みたとき、私を混乱させたのはこの行でした

[groupMapping mapKeyPath:@"users" toRelationship:@"members" withMapping:[[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"users"] serialize:NO];

ここで、最初のキー パスは、参照されているオブジェクトのキー パス マッピングを参照しており (この場合)、マップされているオブジェクトusers内の関係のキー パス (この場合はそうでした) ではありません。groupmembers

この変更により、すべての関係が期待どおりに機能し、満足しています。

于 2012-10-27T20:13:17.010 に答える