2012-06-24 13 views
5

Estoy trabajando en mi primera aplicación RestKit, y uso CoreData para el almacenamiento en caché local. En él tengo una relación de muchos a muchos entre el Post < < --- >> Categoría. La relación se llama publicaciones y categorías.RestKit mapeo many-to-many, ¿cómo?

En mi llamada JSON Post Obtengo id's para las categorías como esta: {"post": [...] "categories":[1,4] [...], donde 1 y 4 son los ids.

tengo un objeto de modelo personalizado Post que hereda de NSManagedObject, en ella tengo una propiedad por categorías, así:

@interface Post : NSManagedObject 

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

que hacer lo mismo en un objeto personalizado Categoría de modelo.

Mis asignaciones Actualmente tiene este aspecto:

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]; 

Cuando ejecuto esto me sale el error: '[<__NSCFNumber 0x6c625e0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key id.'

Realmente agradecería si alguien me podría dar alguna pista sobre cómo asignar esta relación.

Respuesta

2

Error porque su JSON devuelve cada categoría como clave principal, es decir: un NSNumber. Pero su mapeo espera un objeto anidado (que se transformará en un objeto KVC). Cuando su mapa intente acceder a la propiedad "id" en el objeto KVC (que en realidad es un NSNumber), se bloqueará de esta manera.

no estoy seguro de si funcionará, pero se puede intentar algo como esto:

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

[..] 

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

Normalmente, se usaría el [RKManagedObjectMapping connectRelationship: withObjectForPrimaryKeyAttribute:] método, pero no sé si esto funcionará en una relación de muchos a muchos. Puede que tenga que modificar el código fuente para hacer esto. El código a mirar está dentro de [RKManagedObjectMappingOperation connectRelationship:].

De lo contrario, le sugiero que cambie su fuente JSON para que la matriz de categorías sea así: "categorías": [{"categoryID: 1}, {" categoryID ": 4}]. Si la fuente no posible cambiar, puede modificar los datos de forma manual utilizando los métodos de delegado o la extensión RKObjectLoader he dado a continuación:

.h

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

@interface RKObjectLoader (Extended) 

@property (nonatomic, copy) RKObjectLoaderWillMapDataBlock onWillMapData; 

@end 

.m

#import "RKObjectLoader+Extended.h" 

#import <objc/runtime.h> 

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<RKParser> 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<RKObjectLoaderDelegate>*)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<RKObjectMapperDelegate>)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 
+0

Gracias, decidió cambiar el JSON a '" categorías ": [{" category_id ": 1}, {" category_id ": 4}]', ¿sabe cómo y cuándo debería completar la tabla de unión? Actualmente solo alcanzo las categorías. ¡Gracias! – Anders

+0

¿Qué es una tabla de unión? Esta es en realidad otra pregunta. –

+0

Sí, gracias. Una tabla de unión es la tabla entre las dos entidades de muchos a muchos, donde se almacenan las claves primarias de las dos tablas. En realidad, rellena esta tabla de unión cuando cargo publicaciones, pero también crea nuevas filas en mi modelo de Categoría por algún motivo. Probablemente haga otra pregunta sobre ese tema. Pero cambiar el JSON pareció ayudar, ya no hay error. ¡Gracias! – Anders