RestKit veel-naar-veel-toewijzing, hoe?

I'm working on my first RestKit app, and I use CoreData for local caching. In it I have a many-to-many relationship between Post <<--->> Category. The relationship is called posts, and categories.

In mijn Post JSON-aanroep krijg ik id's voor de volgende categorieën: {"post": [...] "categorieën": [1,4] [...] , waarbij 1 en 4 zijn de ID's.

Ik heb een aangepast Bericht modelobject dat erft van NSManagedObject , daarin heb ik een eigenschap voor categorieën, zoals deze:

@interface Post : NSManagedObject

[...]
@property (nonatomic, retain) NSSet *categories;
[...]

Ik doe hetzelfde in een aangepast categoriemodelobject.

Mijn toewijzingen ziet er momenteel als volgt uit:

RKManagedObjectMapping *categoryMapping = [RKManagedObjectMapping mappingForClass:[Category class] inManagedObjectStore:objectManager.objectStore];
categoryMapping.primaryKeyAttribute = @"categoryId";
categoryMapping.rootKeyPath = @"categories";
[categoryMapping mapKeyPath:@"id" toAttribute:@"categoryId"];
[categoryMapping mapKeyPath:@"name" toAttribute:@"name"];
[...]


RKManagedObjectMapping* postMapping = [RKManagedObjectMapping mappingForClass:[Post class] inManagedObjectStore:objectManager.objectStore];

postMapping.primaryKeyAttribute = @"postId";
postMapping.rootKeyPath = @"post";
[postMapping mapKeyPath:@"id" toAttribute:@"postId"];
[...]
[postMapping mapKeyPath:@"created_at" toAttribute:@"createdAt"];
[...]
// Categories many-to-many.
[postMapping mapKeyPath:@"categories" toRelationship:@"categories"  withMapping:categoryMapping];

When I run this I get the error: '[<__NSCFNumber 0x6c625e0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key id.'

Ik zou het erg op prijs stellen als iemand me aanwijzingen kon geven over het in kaart brengen van deze relatie.

5

1 antwoord

De fout is dat uw JSON elke categorie als primaire sleutel retourneert, dat wil zeggen: een NSNumber. Maar uw toewijzing verwacht een genest object (dat wordt omgezet in een KVC-object). Wanneer uw kaart probeert toegang te krijgen tot de eigenschap "id" op het KVC-object (wat eigenlijk een NSNumber is), zal het zo crashen.

Ik weet niet zeker of het zal werken, maar je kunt zoiets als dit proberen:

RKManagedObjectMapping* categoryMapping = //something
categoryMapping.primaryKeyAttribute = @"categoryId";
[categoryMapping mapKeyPath: @"" toAttribute: @"categoryId"];

[..]

[postMapping mapKeyPath: @"categories" toRelationship: @"categories" withMapping: categoryMapping]; 

Normaal gesproken zou u de methode [RKManagedObjectMapping connectRelationship: withObjectForPrimaryKeyAttribute:] gebruiken, maar ik weet niet of dit zal werken op een veel-op-veel-relatie. Mogelijk moet u de broncode wijzigen om dit te doen. De code om naar te kijken bevindt zich in [RKManagedObjectMappingOperation connectRelationship:].

Anders, indien mogelijk, zou ik willen voorstellen dat je je JSON-bron verandert, zodat de array van de categorieën er als volgt uitziet: "categorieën": [{"categoryID: 1}, {" categoryID ": 4}]. Als de bron niet kan worden gewijzigd, u kunt de gegevens handmatig wijzigen met behulp van de gedelegeerde methoden of de RKObjectLoader-extensie die ik hieronder heb gegeven:

.h

typedef void(^RKObjectLoaderWillMapDataBlock)(id* mappableData);

@interface RKObjectLoader (Extended)

@property (nonatomic, copy) RKObjectLoaderWillMapDataBlock onWillMapData;

@end

.m

#import "RKObjectLoader+Extended.h"

#import 

NSString* kOnWillMapDataKey = @"onWillMapData";

@implementation RKObjectLoader (Extended)

- (RKObjectLoaderWillMapDataBlock) onWillMapData {
    return objc_getAssociatedObject(self, &kOnWillMapDataKey);
}

- (void) setOnWillMapData:(RKObjectLoaderWillMapDataBlock) block {
    objc_setAssociatedObject(self, &kOnWillMapDataKey, block, OBJC_ASSOCIATION_COPY);
}

- (RKObjectMappingResult*)mapResponseWithMappingProvider:(RKObjectMappingProvider*)mappingProvider toObject:(id)targetObject inContext:(RKObjectMappingProviderContext)context error:(NSError**)error {
    id parser = [[RKParserRegistry sharedRegistry] parserForMIMEType:self.response.mIMEType];
    NSAssert1(parser, @"Cannot perform object load without a parser for MIME Type '%@'", self.response.mIMEType);

   //Check that there is actually content in the response body for mapping. It is possible to get back a 200 response
   //with the appropriate MIME Type with no content (such as for a successful PUT or DELETE). Make sure we don't generate an error
   //in these cases
    id bodyAsString = [self.response bodyAsString];
    RKLogTrace(@"bodyAsString: %@", bodyAsString);
    if (bodyAsString == nil || [[bodyAsString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) {
        RKLogDebug(@"Mapping attempted on empty response body...");
        if (self.targetObject) {
            return [RKObjectMappingResult mappingResultWithDictionary:[NSDictionary dictionaryWithObject:self.targetObject forKey:@""]];
        }

        return [RKObjectMappingResult mappingResultWithDictionary:[NSDictionary dictionary]];
    }

    id parsedData = [parser objectFromString:bodyAsString error:error];
    if (parsedData == nil && error) {
        return nil;
    }

   //Allow the delegate to manipulate the data
    if ([self.delegate respondsToSelector:@selector(objectLoader:willMapData:)]) {
        parsedData = AH_AUTORELEASE([parsedData mutableCopy]);
        [(NSObject*)self.delegate objectLoader:self willMapData:&parsedData];
    }

    if( self.onWillMapData ) {
        parsedData = AH_AUTORELEASE([parsedData mutableCopy]);
        self.onWillMapData(&parsedData);
    }

    RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:parsedData mappingProvider:mappingProvider];
    mapper.targetObject = targetObject;
    mapper.delegate = (id)self;
    mapper.context = context;
    RKObjectMappingResult* result = [mapper performMapping];

   //Log any mapping errors
    if (mapper.errorCount > 0) {
        RKLogError(@"Encountered errors during mapping: %@", [[mapper.errors valueForKey:@"localizedDescription"] componentsJoinedByString:@", "]);
    }

   //The object mapper will return a nil result if mapping failed
    if (nil == result) {
       //TODO: Construct a composite error that wraps up all the other errors. Should probably make it performMapping:&error when we have this?
        if (error) *error = [mapper.errors lastObject];
        return nil;
    }

    return result;
}

@end
2
toegevoegd
Bedankt, besloten om de JSON te veranderen in "categorieën": [{"category_id": 1}, {"category_id": 4}] , weet u hoe en wanneer ik de join-tabel moet vullen? Ik haal momenteel alleen de categorieën op. Bedankt!
toegevoegd de auteur Anders, de bron
Ja bedankt. Een join-tabel is de tabel tussen de twee veel-op-veel entiteiten, waar primaire sleutels uit de twee tabellen worden opgeslagen. Het vult deze join-tabel eigenlijk in als ik berichten laad, maar dan maakt het ook om een ​​of andere reden nieuwe rijen in mijn categoriemodel. Zal waarschijnlijk een andere vraag voor dat probleem stellen. Maar het aanpassen van de JSON leek te helpen, geen fout meer. Bedankt!
toegevoegd de auteur Anders, de bron
Heeft een nieuwe vraag gesteld voor de opmerking hierboven: stackoverflow.com/questions/11227548/… .
toegevoegd de auteur Anders, de bron
Wat is een join-tabel? Dit is eigenlijk een andere vraag.
toegevoegd de auteur Paul de Lange, de bron