MagicalRecord: one to many relationship - ios

I m not quite sure if it is the right approach
I want to have a one to many relationship between entity A and B, so A could refer to multiple records of B, but B only to one record of A
my question is how to do this with MagicalRecords... i m familiar with the basic fetching and creating of single entities
but have no clue how to fetch, creat, update entities with realtionships

First of all, you need to beware of the 'context' of CoreData. I usually use [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {}]; method, and create entity inside the block.
I will use my case i show above as an example,
To save a "message" to a "conversation" entity,
ChatConversation* chatConversation = [ChatConversation MR_findFirstByAttribute:#"conversationID" withValue:<your value> inContext:localContext];
^first see if there is a entity already been created using the find first by attribute method
ChatMessage* chatMessage = [ChatMessage MR_createInContext:localContext];
[chatMessage set.....];
......
if(!chatConversation){
//You need to create a new chatConversation entity
chatConversation = [ChatConversation MR_createInContext:localContext];
......
}
[chatMessage setChatConversation:chatConversation];
If you want to get all message from a chat conversation, just fetchAll messages with custom predicates
-(NSFetchedResultsController *)fetchedResultsController {
if(!_fetchedResultsController)
_fetchedResultsController = [ChatMessage MR_fetchAllSortedBy:#"createdAt" ascending:TRUE withPredicate:[NSPredicate predicateWithFormat:#"%K == %#", #"conversation.conversationID",self.conversationObject.conversationID] groupBy:nil delegate:self];
return _fetchedResultsController;
}

What do you mean by "how to do this with MagicalRecords"? Cause this is not handle by MagicalRecords.
They are all set in the coredata model file.
For instance, for my project, i am implementing the IM feature.
1. One conversation -> many messages
2. One message ->one conversation
this is the design of data model.
feel free to ask me any questions about Coredata + MR
Cheers!

Related

CoreData relationship between objects

I have two kind of objects that I retrieve from a server:
1) Companies
2) Categories
I am updating the data from the server each time that the app is become active(deleting the old data and inserting the new data). From my database Categories and Companies are connected between them with the category_id.
My question here is can I connect with a relationship each company with a category using the category_id?
I assume that means that each Company has a relationship with only one particular Category.
You want to add categoryId to your Category entity, and save the ID when you add the category.
Your models would include a number of other attributes, but here is how the relationships would be setup.
And, the relationships in the inspector...
Of course, you should specify any other options as you deem necessary.
Now, you set the category relationship of a Company entity by searching for the Category based on CategoryId, and then simply setting the category relationship. Core data will automatically take care of the inverse relationship.
Now, given any Category, you can know all the Company objects that are in that category with the `categories relationship, and given any Company you can know which Category it is in.
To fetch a Category by ID, you could do something like this...
- (NSManagedObject*)categoryWithCategoryId:(NSString*)categoryId
inMOC:(NSManagedObjectContext*)moc
error:(NSError **)error {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Category"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"categoryId = %#", categoryId];
fetchRequest.fetchLimit = 1;
return [[moc executeFetchRequest:fetchRequest error:error] firstObject];
}
And to set the relationship, if you have xcode/mogenerator generated subclasses, just set the relationship property.
NSError *error = nil;
Category *category = [self categoryWithCategoryId:categoryId inMOC:moc error:&error];
if (category) {
// If you have generated subclasses...
company.category = category;
// Otherwise, you can do...
[company setValue:category forKey:#"category"];
}

How to get data from unrelated entities using Core Data?

I have these three entities which are not related. How can i get the salary of an employee. I want something like this :
Select C.salary FROM Employee A, Department B, Salaries C Where A.id=B.empid AND B.id=C.deptid AND A.id=12
i need to do the same above operation in core data.
As Martin suggested, the easy way is to set up the needed relationships and traverse them.
But if you don't have the permission to alter the model, you need to work and filter managed in memory object you own once retrieved. In other words, you need to set up NSFetchRequests with the correct NSPredicates.
For example, to retrieve the department for a given employee.
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:#"Department"];
request.predicate = [NSPredicate predicateWithFormat:#"empid", #(12)];
NSError *error = nil;
NSArray *departments = [moc executeFetchRequest:request error:&error];
if(departments) {
// do a NSLog for departments here to see what you have
// here you can access deptid for each department you retrieve
// and with that value run another request against Salaries
// e.g. NSManagedObject* department = [departments lastObject];
// NSNumber* deptid = [department valueForKey:#"deptid"]
} else {
// handle error here
}
Other part is left as excersise ;)

Nested core data fetch

I have a core data DB with multiple entities. There is a parent entity called "Person" which has many "Friends", "Friends" have many "Activities", and "Activities" have "Types" ("Types has a to-many relationship to "Activities"). What I'm trying to achieve is filtering all the "Person" entities by "Types". A user would be tapping on a "Type" and then I would refresh my table and filter the "Person" entities that are displayed by the "Types" that are associated with them.
Currently I'm thinking I have to use a compound predicate but I'm completely sure how to go about it. So far all I've done is printed out the values I've wanted by looping through my fetchedObjects like so:
NSArray *persons = self.fetchedResultsController.fetchedObjects;
for (JIPerson *person in persons) {
JIFriend *friend = person.friends.anyObject;
JIActivity *activity = friend.activities.anyObject;
JIType *type = activity.type;
NSLog(#"%#", type.name);
}
This prints out the values correctly, but I need to filter my table using these values. How can I achieve that?
Seems like I got it. Using NSPredicate you can traverse a deep relationship like this using dot notation. My implementation went as follows:
- (void)filterPersonByType:(NSString *)typeName {
NSPredicate *typePredicate = [NSPredicate predicateWithFormat:#"ANY friends.activities.type.name CONTAINS[cd]%#", typeName];
}
Yes, you can use dot notation with NSPredicate. You might want to have the type be an actual type, though, that would be cleaner than just doing string comparisons.

Core Data Adding object in 1 to many relationship gives error

I have two Entities one called Games and one called Teams. The Games entity has a to one relationship to Teams called teams and the Teams entity has a to many relationship to Games called games. (A team can be in many games but a game can only have 1 team. I am using a separate entity for Opponents)
I am selecting a team by using it's ID. Here is my code for adding a team to the Games entity:
Games *newGame = (Games *) [NSEntityDescription insertNewObjectForEntityForName:#"Games" inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *fetchTeams = [[NSFetchRequest alloc] init];
NSEntityDescription *fetchedTeam = [NSEntityDescription entityForName:#"Teams"
inManagedObjectContext:self.managedObjectContext];
[fetchTeams setEntity:fetchedTeam];
NSArray *fetchedTeams = [self.managedObjectContext executeFetchRequest:fetchTeams error:&error];
for (Teams *myTeam in fetchedTeams) {
if (myTeam.teamID == teamid){
newGame.teams = myTeam;
}
}
The error I am getting is: 'NSInvalidArgumentException', reason: 'The left hand side for an ALL or ANY operator must be either an NSArray or an NSSet.'
I don't understand it, newGame.teams is an object of Teams , it is not an NSSet. If I was doing Teams.games it would be an NSSet.
What am I doing wrong?
You've not described what is the data type of variable "teamid" here. Hence, I'm assuming that it must be some primitive type int.
Based on this assumption, you can make the following changes in your code:
if(fetchedTeams!=nil)
{
if(fetchedTeams.count>0)
{
for(Teams *myTeam in fetchedTeams)
{
//check here because coredata stores a number in NSNumber object.
//Hence you've to get the intValue to make a equality check, like below
if(myTeam.teamID.intValue == teamid)
{
//do your stuff here.
//Also check the last part of my answer, I have a question here.
}
}
}
}
In your code you have "newGame.teams = myTeam". What is the data type of "teams" in "newGame" ? is it Teams* or NSSet* ?

Programmatically update duplicate values in coredata?

I am new in Core Data. I want to update duplicate values. For example my table looks like this
id | Name
============
1 | Joseph
2 | Fernandez
3 | Joseph
4 | James
Say that I want to update Joseph corresponding to id 1 and 4 to "myName". When I tried to update this it only updates the 4th row. I can't find any way to do this in any of the documentation. Can anyone suggest me a solution?
One more question, how can I print all name values?
you will have to read over the documentation to know how to update record
http://www.appcoda.com/core-data-tutorial-update-delete/
James,
I'll try to reply to both your questions with sample code.
To update specific objects you need to se up a new NSFetchRequest with a predicate, grab the objects (of type NSManagedObject), update the values you are interested in and save the context.
So, for example:
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"YourEntityName"];
// set the predicate (it's equal to set a WHERE SQL clause) filtering on the name for example
// use camel case notation if possible, so instead of Name use name (for this you have to changes your model, if you don't want to do it use Name)
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"name == %#", #"Joseph"]];
NSError* error = nil;
NSArray* results = [context executeFetchRequest:fetchRequest error:&error];
// do some error checking here...
for (NSManagedObject resultItem in results) {
// use KVC (for example) to access your object properties
[resultItem setValue:#"myName" forKey:#"name"];
}
// save your context here
// if you don't save, changes are not stored
To print you need to se up a new NSFetchRequest, grab the objects (of type NSManagedObject) and use NSLog.
So for example:
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"YourEntityName"];
NSError* error = nil;
NSArray* results = [context executeFetchRequest:fetchRequest error:&error];
// do some error checking here...
for (NSManagedObject resultItem in results) {
NSLog(#"%#", [resultItem valueForKey:#"name"]);
}
P.S. The code I provided is quite simple and the predicate I used to specific values check against the name. Since this could be error prone, I would modify the model and using a sort of guid for each objects you need to use (I don't know if id is for that but I would change its name to another one, for example userId). Once done you can check against it.
Hope that helps.
It's as simple as retrieving the NSManagedObject and changing the Name property. You can retrieve the NSManagedObject with a fetch request. Once you changed the property and you want to keep it changed even when you close the application you'll have to do a save on the managedObjectContext.
You'll have to read over the documentation to get up to speed on core data:
http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/Articles/cdBasics.html#//apple_ref/doc/uid/TP40001650-TP1
Edit: just NSLog whatever you want to know, for example log you fetch request results.

Resources