Reputation: 4764
For a detail view I would like to let the user leave notes for each item. The app is for a data-driven website. In the web version, the web app stores notes in a separate table with a field for the itemid.
In Core Data I have an entity of items and another entity of notes. The notes entity has an attribute called itemid
. When user creates a note the first time, it stores the itemid
in the note record.
My question is when you pull up the item for editing how can you simultaneously pull up the right note based on the note having a certain itemid
?
In a database situation you could do a join, or from a web page you could make two separate requests to the two tables but I am somewhat flummoxed by how to do this with Core Data.
Do you have to put a relationship to the note and therefore have the noteid
in the item row?
If so would you be able to access the note attribute using the item object?
Thanks in advance for any suggestions.
This is what I am using to save information. I just don't know how to make sure I'm saving it for right note.
self.managedObjectContext = [IDModel sharedInstance].managedObjectContext;
NSString *noteText = _notesView.text;
NSNumber *itemId = self.item.itemid;
// Populate Record
[self.note setValue:noteText forKey:@"note"];
[self.note setValue:itemId forKey:@"itemid"];
Model (simplified):
Item:
name NSString
itemid: Integer 64
Note:
note NSString
noteid: Integer 64
itemid: Integer 64
Edit:
Code to try to link note and item while creating both...
//in save method
// Create Entity
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Notes" inManagedObjectContext:self.managedObjectContext];
// Initialize New Record ie newNote
NSManagedObject *record = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext];
// Populate Record
[record setValue:note forKey:@"note"];
[record setValue:localid forKey:@"localnid"];
// Save Record
NSError *error = nil;
if ([self.managedObjectContext save:&error]) {
// If note saved, save new item…
if (itemlength>1) {
Items *newItem = [NSEntityDescription insertNewObjectForEntityForName:@“Item” inManagedObjectContext:self.managedObjectContext];
newItem.item = item;
newItem.note = self.note
//This is null as note does not seem to pick up newly created note.
if (![self.managedObjectContext save:&error]) {
NSLog(@"Error: %@", [error localizedDescription]);
}
}
Upvotes: 0
Views: 103
Reputation: 21536
Yes, you should use a relationship between the Item
and Note
entities. To create a relationship just Ctrl-drag from one entity to the other in the data model editor. Note that Xcode automatically adds an inverse relationship:
I've renamed the relationships for clarity - you can tailor the details of the relationship (name, one-one v one-many, delete rule, etc) in the panel on the right. In the example above, the Item
entity has three properties: 2 attributes and 1 relationship. Given an Item
object, say myItem
, the values for these properties can be accessed using the Key Value Coding methods: valueForKey:
and setValue:forKey:
. For example, if attribute
is defined as a string:
NSString *myStringValue = [myItem valueForKey:@"attribute"];
[myItem setValue:@"new value for attribute" forKey:@"attribute"];
That's very long-winded. So to make life easier, use the "Create NSManagedObject subclass..." option. Xcode will configure each entity to be a subclass of NSManagedObject and will create new class files (.h/.m or .swift) with details of the properties. For the example Item
:
@property (nullable, nonatomic, retain) NSString *attribute;
@property (nullable, nonatomic, retain) NSString *attribute1;
@property (nullable, nonatomic, retain) Note *note;
The thing to realise is that the note
relationship is of class Note. It's not a foreign key, or a noteid, that you have to use to lookup the corresponding Note
object. It is the Note object. Under the hood, CoreData is adding primary keys and foreign keys to the underlying tables, but all that aggravation is abstracted away.
Having created the subclasses, you can use the dot-accessors for the object properties:
NSString *myStringValue = myItem.attribute;
myItem.attribute = @"new value for attribute";
For the relationship, if you have an Item
object called myItem
and a Note
object called myNote
, you can set the relationship value with:
myItem.note = myNote;
or equivalently:
myNote.item = myItem;
(Note: use one or the other, not both; CoreData automatically sets inverse relationships for you).
Now, you have the added complication of a web server from which the Item objects and Note objects are downloaded. And on your web server, your Notes
table has a field for the itemid
, which is used to link Items and Notes. At some point, you want to link up Note objects and Item objects using the itemid. The usual approach would be to do it once (as soon as the CoreData objects are synchronised from the server), set the relationship accordingly, and thenceforth use the relationship rather than the itemid to get the note for a given item. For example, if you are creating a new Note
object, and the itemid from the server is "1234", you might do this:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Item"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"itemid == %@", @"1234"];
NSError *error;
NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
// should check for nil results/error
if (results.count > 0) {
// found (at least) one Item with itemid == @"1234"
// use the first to set the relationship
newNote.item = results[0];
}
Then whenever you have a particular Item object, you can access the corresponding note using
Note *myNote = myItem.note;
Furthermore, you can cascade the dot-notation, so get the value of attribute
for the Note for myItem, use:
NSString *noteText = myItem.note.attribute;
EDIT
Your save method is very close: either set self.note = record
before you save, or use newItem.note = record
:
//in save method
// Create Entity
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Notes" inManagedObjectContext:self.managedObjectContext];
// Initialize New Record ie newNote
NSManagedObject *record = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext];
// Populate Record
[record setValue:note forKey:@"note"];
[record setValue:localid forKey:@"localnid"];
// Save Record
NSError *error = nil;
if ([self.managedObjectContext save:&error]) {
// If note saved, save new item…
if (itemlength>1) {
Items *newItem = [NSEntityDescription insertNewObjectForEntityForName:@“Item” inManagedObjectContext:self.managedObjectContext];
newItem.item = item;
newItem.note = record;
if (![self.managedObjectContext save:&error]) {
NSLog(@"Error: %@", [error localizedDescription]);
}
}
}
Upvotes: 1
Reputation: 33428
To reach your goal you need to create a NSFetchRequest
passing it the right NSPredicate
.
The fetch request will be run against your Note
entity. The predicate will allow you to specify that the Note
object you want to retrieve is the one for that specific noteid
.
So, if you have a 1-to-1 relationship between Item
and Note
, the NSPredicate
should like the following:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"relationshipToItem.propertyForItemId == %@", yourItemId];
[request setPredicate:predicate];
Here I suppose you've a created a relationship between the two entities, otherwise you need to do it manually. Can you provide how your model looks like?
Upvotes: 0