PDA

View Full Version : Adding a Core Data entity "object" to another!




Littleodie914
Mar 23, 2007, 05:09 PM
Hey guys, here's my situation. Without spoiling too much of the surprise, I've got a Core Data application that has (in the data model) two Entities of type Assignment and Archive.

An Assignment entity has a few attributes, such as an NSString name, a Date dueDate, and a few other types of things. What I'm wondering is how to take an assignment object and "move" it to the archive entity. I know this sounds weird, as my terminology probably isn't quite right.

Essentially, I've achieved this "somewhat" with the below code:

NSManagedObjectContext * context = [[NSApp delegate] managedObjectContext];
NSManagedObject * myObject = [[assignmentController selectedObjects] objectAtIndex:0];

myObject = [NSEntityDescription insertNewObjectForEntityForName: @"ArchiveAssignment" inManagedObjectContext: context];

[myObject setValue:[[[assignmentController selectedObjects] objectAtIndex:0] valueForKey:@"name"] forKey:@"name"];
[myObject setValue:[[[assignmentController selectedObjects] objectAtIndex:0] valueForKey:@"priority"] forKey:@"priority"];
[myObject setValue:[[[assignmentController selectedObjects] objectAtIndex:0] valueForKey:@"dueDate"] forKey:@"dueDate"];

[archiveAssignmentController addObject:myObject];
[assignmentController removeObjectAtArrangedObjectIndex: [assignmentTableView selectedRow]];

Where assignmentController and archiveAssignmentController are the NSArrayController linked to the two entities. The way above works, but I have to set all of the keys manually, and each Assignment actually has a "To-Many" relationship to another entity that I would like to transfer as well. What I was thinking was something like:

[archiveController addObject:[[assignmentsController selectedObjects] objectAtIndex:0]];
[assignmentsController removeObjectAtArrangedObjectIndex:[assignmentTableView selectedRow]];

But even though this compiles fine, it doesn't quite work right. Any ideas? Thanks so much in advance, I don't know what I'd do without you guys. :D



Eraserhead
Mar 23, 2007, 07:14 PM
OK, I don't think you are doing this in the best way.

What about trying keeping one type of object, "assignment", then add a BOOL property to it, called "archive", then on your two array controllers, on the first set the filter predicate to archive==0, and the second set the filter predicate to archive==1.

If you want to do searching you can't use bindings and have to create a method a little bit like this (it searches for a property called "name".

-(IBAction)searchArray:(id)sender{
NSString *searchText=[sender stringValue];
NSString *filteredPred=@"archive==1"
NSLog(@"searchText=%@",searchText);
if([searchText isEqualToString:@""] || searchText==nil){
[arrayController setFilterPredicate:[NSPredicate predicateWithFormat:filteredPred];
}else{
[arrayController setFilterPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@ AND \"%@\" IN[c] name",filteredPred,searchText]]];
}
}

Littleodie914
Mar 24, 2007, 03:56 PM
Alright, I see what you're saying, but I'm having a bit of trouble with the filterPredicate. What do I bind the predicate to? Here's what I'm trying right now, is this what you meant? :)

Littleodie914
Mar 24, 2007, 04:28 PM
Hmm... I've changed my mind, and decided to try it the old way. Now, I'm getting an error though, and I'm pretty sure it's because I'm using a Document-based Core Data application instead of just a Core Data app. Do you know what I would have to change in my original code to get rid of this:

+entityForName: could not locate an NSManagedObjectModel

Again, thanks so much for helping a very novice Cocoa dev! :D

Eraserhead
Mar 25, 2007, 09:28 AM
Alright, I see what you're saying, but I'm having a bit of trouble with the filterPredicate. What do I bind the predicate to? Here's what I'm trying right now, is this what you meant? :)

Not there, in the Attributes box: ;).

70881


+entityForName: could not locate an NSManagedObjectModel

I have had those errors, too, I believe the problem is probably some fetch requests you are doing, the real problem is that CoreData is still a bit buggy (it was only released with 10.4 ;) ), and the only reliable way to implement them is using:

[mObjC executeFetchRequest:[model fetchRequestFromTemplateWithName:@"fetchRequest" substitutionVariables:[NSDictionary dictionary]]
error:nil]
where mObjC is the NSManagedObjectContext and model is the NSManagedObjectModel. You then have to have a fetch request (in this case called "fetchRequest") in the data model.

Littleodie914
Mar 25, 2007, 02:53 PM
Alright... This is starting to get weird. I'm realizing that your recommended method is indeed easier (and makes more sense) but now my problem is, the below code compiles fine, but isn't my goal:

[[[assignmentsController arrangedObjects] objectAtIndex:[assignmentTableView selectedRow]] setValue:NO forKey:@"archive"];

My goal is this, which causes a warning (makes pointer from integer without a cast):

[[[assignmentsController arrangedObjects] objectAtIndex:[assignmentTableView selectedRow]] setValue:YES forKey:@"archive"];

Isn't that weird that changing the value of the boolean would do that? Maybe I've got something messed up! :confused:

Eraserhead
Mar 25, 2007, 05:51 PM
I'm guessing this is happening because C (which Objective C/Cocoa is based on) doesn't have boolean values, so NO=0 and YES=1, basically I'm thinking that assigning a value of 0 is the same as assigning a value of nil or something, which is allowed. (It happens for me too)

The reason you are getting errors with the YES version is that that isn't zero so it's an integer, which isn't an object, and when you are setting a value using setValue: forKey: you must set an object. So you have to use NSNumber (http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/Reference/Reference.html) to do it, the code you are looking for is:

[[[assignmentsController arrangedObjects] objectAtIndex:[assignmentTableView selectedRow]] setValue:[NSNumber numberWithBool:YES] forKey:@"archive"];

Good luck with it!

Spike099
Mar 25, 2007, 06:15 PM
Going through the book I have, a NSButton returns NSOffState (0) and NSOnState (1) when you call the method state. Maybe you could replace the
[NSNumber numberWithBool:YES]
with
NSOnState
?

Eraserhead
Mar 25, 2007, 06:20 PM
Going through the book I have, a NSButton returns NSOffState (0) and NSOnState (1) when you call the method state. Maybe you could replace the
[NSNumber numberWithBool:YES]
with
NSOnState
?



I'm afraid not, they are equivalents to integers (as it says in the documentation under NSCellState, though a search for NSOnState takes you to the right place.), whereas we need an object here.

Spike099
Mar 25, 2007, 06:29 PM
Ah interesting. I had searched for NSOnState in the documentation but came up with nothing. Should have realized that C had to be included in languages to search. Thanks.

Littleodie914
Mar 26, 2007, 09:13 AM
Gah... This is starting to get weird. I've got two controllers:

archiveController (Entity: Assignment, Predicate: archive==1)
assignmentsController (Entity: Assignment, Predicate: archive==0)

There are two NSTableViews, and two NSButtons.

Archive Button: sets archive to 1
Unarchive Button: sets archive to 0

The two tableViews are bound to the controllers, to display the values in each. Here's a scenario, and what happens:

I make a new assignment, and via NSLog prompts I can tell its archive attribute is 0. Displays in the assignmentsController and the respective TableView. I hit the archive button, it changes the archive to 1, and it shows up in the archive TableView. Problem is, it doesn't remove itself from the original table view. So I click on the assignment in the archive TableView, hit Unarchive, and bam, archive is set to 0, and it's removed from the archive TableView.

So what's happening is when archive is set to 1, it "adds" it to the archive TableView, but doesn't remove it from the assignment TableView. So the filter predicate is working for archives, but not for assignments. Any ideas? :confused:

Eraserhead
Mar 26, 2007, 09:22 AM
It could be that "automatically prepares content", is checked on one controller but not the other, if not you can try reloading the data in the troublesome table with reloadData (in NSTableView). If not I've sent you a PM with my email address so if this doesn't sort it send me your code in an email and I'll take a look.