2010-10-07 7 views
7

Tengo un problema para averiguar cómo representar un modelo de relación muchos a muchos en un NSTokenField. Tengo dos modelos (relevantes):NSTokenField que representa la relación Core-to-many de datos

artículo Tag

Un elemento puede tener muchas etiquetas y una etiqueta puede tener muchos elementos. Entonces, es una relación inversa a-muchos.

Lo que me gustaría hacer es representar estas etiquetas en un NSTokenField. Me gustaría terminar con un tokenfield sugiriendo automáticamente coincidencias (descubrí una forma de hacerlo con tokenfield: completionsForSubstring: indexOfToken: indexOfSelectedItem) y poder agregar nuevas entidades de etiqueta si no se combinó con una existente.

De acuerdo, espero que todavía estés conmigo. Estoy tratando de hacer todo esto con los enlaces y controladores de array (ya que tiene más sentido, ¿no?)

Tengo un controlador de array, "Controlador de matriz de elementos", que está vinculado a mi aplicación delega managedObjectContext. Una vista de tabla que muestra todos los elementos tiene un enlace a este controlador de matriz.

El valor de mi NSTokenField tiene una vinculación con la clave de selección de los controladores de matriz y la ruta de la clave de modelo: etiquetas.

Con esta configuración, NSTokenField no mostrará las etiquetas. Simplemente me da:

<NSTokenFieldCell: 0x10014dc60>: Unknown object type assigned (Relationship objects for {(
    <NSManagedObject: 0x10059bdc0> (entity: Tag; id: 0x10016d6e0 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Tag/p102> ; data: <fault>) 
)} on 0x100169660). Ignoring... 

Esto tiene sentido para mí, así que no se preocupe. He mirado en algunos de los métodos de delegado NSTokenField y parece que yo debería usar:

- (NSString *)tokenField:(NSTokenField *)tokenField displayStringForRepresentedObject:(id)representedObject 

El problema es que este método no se llama y me da el mismo error que antes.

Muy bien, así que mi siguiente movimiento fue intentar hacer un ValueTransformer. Transformar desde una matriz con entidad de etiqueta -> matriz con cadenas (nombres de etiqueta) fue todo bien. La otra forma es más desafiante.

Lo que he intentado es buscar cada nombre en el contexto de objeto administrado de delegado de mi aplicación compartida y devolver las etiquetas coincidentes. Esto me da un problema con diferentes contextos de objetos administrados aparentemente:

Illegal attempt to establish a relationship 'tags' between objects in different contexts (source = <NSManagedObject: 0x100156900> (entity: Item; id: 0x1003b22b0 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Item/p106> ; data: { 
author = "0x1003b1b30 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Author/p103>"; 
createdAt = nil; 
filePath = nil; 
tags =  (
); 
title = "Great presentation"; 
type = "0x1003b1150 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Type/p104>"; 
}) , destination = <NSManagedObject: 0x114d08100> (entity: Tag; id: 0x100146b40 <x-coredata://9D77D47A-1171-4397-9777-706F599D7E3B/Tag/p102> ; data: <fault>)) 

¿Dónde me equivoco? ¿Cómo resuelvo esto? ¿Es incluso el enfoque correcto (me parece raro que tengas que usar un ValueTransformer?)

¡Gracias de antemano!

+0

Pasé más tiempo investigando esto hoy, todavía no puedo encontrar recursos que expliquen esto. ¡Espero que alguien venga al rescate aquí! :) – simonwh

Respuesta

7

He escrito una costumbre NSValueTransformer para mapear entre el NSManagedObject/Tag obligado NSSet y la NSStringNSArray del campo token.Estos son los 2 métodos:

- (id)transformedValue:(id)value { 
    if ([value isKindOfClass:[NSSet class]]) { 
    NSSet *set = (NSSet *)value; 
    NSMutableArray *ary = [NSMutableArray arrayWithCapacity:[set count]]; 
    for (Tag *tag in [set allObjects]) { 
     [ary addObject:tag.name]; 
    } 
    return ary; 
    } 
    return nil; 
} 

- (id)reverseTransformedValue:(id)value { 
    if ([value isKindOfClass:[NSArray class]]) { 
    NSArray *ary = (NSArray *)value; 
    // Check each NSString in the array representing a Tag name if a corresponding 
    // tag managed object already exists 
    NSMutableSet *tagSet = [NSMutableSet setWithCapacity:[ary count]]; 
    for (NSString *tagName in ary) { 
     NSManagedObjectContext *context = [[NSApp delegate] managedObjectContext]; 
     NSFetchRequest *request = [[NSFetchRequest alloc] init]; 

     NSPredicate *searchFilter = [NSPredicate predicateWithFormat:@"name = %@", tagName]; 
     NSEntityDescription *entity = [NSEntityDescription entityForName:[Tag className] inManagedObjectContext:context]; 

     [request setEntity:entity]; 
     [request setPredicate:searchFilter]; 

     NSError *error = nil; 
     NSArray *results = [context executeFetchRequest:request error:&error]; 
     if ([results count] > 0) { 
     [tagSet addObjectsFromArray:results]; 
     } 
     else { 
     Tag *tag = [[Tag alloc] initWithEntity:entity insertIntoManagedObjectContext:context]; 
     tag.name = tagName; 

     [tagSet addObject:tag]; 
     [tag release]; 
     } 
    } 
    return tagSet; 
    } 
    return nil; 
} 

CoreData parece establecer de forma automática las relaciones de objeto en el retorno (pero no se han verificado por completo esta aún)

espero que ayude.

0

2 preguntas:

1) ¿Tiene una NSManagedObjectContext siendo utilizado con fines distintos del contexto delegado de la aplicación? 2) ¿Es el objeto que implementa tokenField: displayStringForRepresentedObject: establecido como el delegado para NSTokenField?

1

Su segundo error se debe a que tiene dos contextos de objetos gestionados separados con el mismo modelo y tienda activos al mismo tiempo. Está intentando crear un objeto en un contexto y luego relacionarlo con otro objeto en el segundo contexto. Eso no está permitido. Necesita perder el segundo contexto y establecer todas sus relaciones dentro de un contexto único.

Su error inicial es causado por una ruta de tecla incompleta. Según su descripción, parece que intenta rellenar los campos de token con ItemsArrayController.selectedItem.tags, pero que devolverá un objeto Tag que el token archivado no puede usar. En cambio, debe proporcionarle algo que se convierta en una cadena, p. ItemsArrayController.selectedItem.tags.name

Cuestiones relacionadas