views:

46

answers:

3

Okay, here's the situation. I have two NSManagedObjects called 'Store' and 'Aisle'. There is a one-to-many relationship from Store to Aisle and a one-to-one relationship from Aisle to Store.

I have existing stores added in Core Data that I can access just fine. My problem is that I cannot figure out how to add an Aisle object to an existing Store. I've tried fetching the Store and then adding an Aisle object to the .Aisles set, I've tried using the AddAislesObject: method provided by CoreData in Store.h, and I've tried adding a Store object to the .Stores set in the Aisle object.

I'm watching my SQLite database and it looks like the Aisles are being added successfully and that they are associated to the correct Store using the Core Data primary key. But for some reason when I try to retrieve the Aisle objects (using the .Aisles property of the fetched Store object) I always get a zero-count NSSet.

I've also tried deleting my .sqlite file to no avail.

Does anyone have some simple code or even a high level suggestion for the best way to do this?

Thank you!

--- UPDATE 8/30 0917 CST

I've taken some of Adam's suggestions below and my aisles are definitely saving into Core Data correctly. However, when I try to access the saved Aisle from the Store object, I'm not getting anything returned. In the code below you can see that I'm retrieving an existing Store object from Core Data using a name and location (there are safeguards on insert to make sure that this combination is unique). I then use some NSLog debugs to access the single Store in the returned array, and the aisles property to iterate through any aisles. Ignore everything below the debug loops because I haven't finished the method yet.

What I'm seeing is that aisleList has zero objects in it.

-(NSArray *)getAisleListByStore: (Store *)aStore {

// this method should return an NSArray of aisles for aStore

NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Store" inManagedObjectContext:context];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name MATCHES[c] %@ AND location MATCHES[c] %@", aStore.name, aStore.location];

[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];

NSError *error;
NSArray *storesList = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];

if (storesList == nil || [storesList count] != 1) {

    // error detected
    NSLog(@"Error retrieving stores: %@", [error localizedDescription]);
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Error retrieving stores" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
    [alert show];
    [alert release];
    return nil;

} else {

    // get aisles

    // debugs
    for (Store *theStore in storesList) {
        NSLog(@"Name: %@", theStore.name);
        NSLog(@"Aisle count: %d", [theStore.aisles count]);
        NSSet *aisleList = theStore.aisles;

        for (Aisle *theAisle in aisleList) {
            NSLog(@"Aisle name: %@", theAisle.name);
        }
    }

    // Store *theStore = [storesList objectAtIndex:0];

    // NSArray *aisles = (NSArray *)theStore.aisles;
    // return aisles;
}

}

A: 

How about slipping a third table between these two tables to connect them? This pairs Stores with Aisles ids and provides the eitherway connections. Call it StoreAisle.

You have a store and want the aisles? Find the store you want in the store table, take that store ID, look in the StoreAisle table to find the IDs for the aisles from the Aisles table.

You have an aisle and want the store? Find the aisle that you want in the aisle table, take that ID and look in the StoreAisle table for the store ID that you then look in the Store table.

No one in particular
I've actually considered this, or at least using the PK identifiers to write my own query so that I don't have to rely on using the Store object to access the aisles. I don't think this is the "right" way though. Do you know how I would go about retrieving the Core Data generated PKID of an object?
Haeriphos
+1  A: 

No need for a third table.

You probably have a NSSet* in Store, but you have an Store* attribute in Aisle. This is one huge advantage of following Core Data's advice about making every relationship have an inverse.

Store* store = [NSEntityDescription insertNewObjectForEntityForName:@"Store" inManagedObjectContext:moc];
Aisle* aisle = [NSEntityDescription insertNewObjectForEntityForName:@"Aisle" inManagedObjectContext:moc];

Assuming the Aisle entity has the relationship inStore as an inverse of Store's to-many aisles relationship:

aisle.inStore = store;

You'll find Core Data takes care of the other side of the relationship, i.e. adding the Aisle to Store's NSSet.

Don't forget:

  NSError *error;
  if (![moc save:&error]) 
  {
    NSLog(@"Core Data Save error %@, %@", error, [error userInfo]);
  }

to make your changes permanent.

Adam Eberbach
Thanks Adam. I was using this general format when saving the aisle but I made a few tweaks based on your examples. The aisles are definitely saving in Core Data, but I'm still having problems retrieving them from the Store object. I'll post some more in my original question above.
Haeriphos
The problem was actually something with the data model. I deleted it and recreated it (with no changes) and it works fine now.
Haeriphos
+2  A: 

First off, is your relationship from Store to Aisle actually called Aisles or is it called aisles or aisle? Make sure all your spelling is correct. Also, the relationship should be called aisles as it is a relationship, not an entity and it is to-many. You should also make sure that the to-one relationship from Aisle to Store (that should be called store) is the inverse relationship of the to-many Store to Aisle relationship. Then if you grab a Store entity and use NSSet *aisleSet = storeEntity.aisles; you should get a correct set.

theMikeSwan
The relationship from Store to Aisle is called aisles and I just changed the name of the relationship from Aisle to Store to be inStore. They're configured as inverse relationships to each other. However, it's still not working. I see the Aisle entry in SQLite but cannot retrieve it using the Store object.
Haeriphos