CoreData relationship between objects - ios

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"];
}

Related

NSFetchedResultController complex sort case

I have following data model:
[Agency] <--->> [AgencyRating] <<---> [RatingType]
There are numbers of rating types (ex. overall, branding, design, programming, etc). Each agency can have all these rating types (ex. agency FooBar has 1050 points in design and 700 points in branding).
Tables structures:
Agency: name:string
RatingType: name:string
AgencyRating: agency_ref: relationship to agency, ratingType_ref: relationship to rating type, value: Integer.
I have split view controller. Left side contains available rating types. When user picks any rating type, I'd like to show sorted by rating value agencies on the right side.
I'd like to achieve it with NSFetchedResultController. I understand how to get rating values to display it in cells, but I don't understand how to make a sort descriptor.
Currently I have:
Controller:
_contentFetchController = [Agency MR_fetchAllGroupedBy:nil withPredicate:[NSPredicate predicateWithValue:YES] sortedBy:#"name" ascending:YES];
Cell:
- (void) configureForAgency:(Agency *) agency ratingType:(RatingType *) ratingType forIndexPath:(NSIndexPath *) path {
self.positionLabel.text = [NSString stringWithFormat: #"%d", (int)path.row + 1];
UIImage *image = [UIImage imageWithContentsOfFile:agency.logoPath];
if (image) {
self.logoImageView.image = image;
}
self.nameLabel.text = agency.name;
// HERE IS VALUE OF RATING
self.ratingValueLabel.text = [[agency ratingForRatingType:ratingType] stringValue];
}
As you can see: I sort by name, but I need to sort by rating value, based on selected rating type. Any ideas?
In order to do that you need to fetch Agencies, that have an associated AgencyRating with a selected rating type and sort everything by rating value.
I assume that rating type is ratingType, rating value is ratingValue and AgencyRating association is agencyRatings. I also assume that each agency can have only one agency rating of each rating type (if not, you will have to sort by the SUM of all agency ratings of selected type).
Judging by the MR_ prefix, I guess you are using MagicalRecord, which I'm not familiar with, but here's how you can do that with plain NSFetchRequest and NSFetchedResultsController.
First you will need to set up a NSFetchRequest:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Agency"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"ANY agencyRatings.ratingType = %#", ratingType];
fetchRequest.sortDescriptors = #[ [NSSortDescriptor sortDescriptorWithKey:#"agencyRatings.ratingValue" ascending:YES] ];
Then you can set up the NSFetchedResultsController as you would normally do providing it with the fetchRequest you just created.
Every time the user changes the ratingType you will need to change the fetchRequest's predicate and call performFetch on the fetchedResultsController.
I guess you need to adjust this a little bit to fit your case, but the idea is to to have a predicate and a sort descriptor that are pointing to a related table.

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 ;)

NSPredicate for obtaining all Students with a certain Course name?

I have two Entities, Student and Course, where each has a to-many relationship to the other (i.e., a Student has 0 or more Courses, and a Course has 0 or more Students).
I need to obtain all Students who are enrolled in a Course. I also need to obtain all Courses in which a given Student is enrolled (by firstName).
An pseudo-predicate would be: all students who have a course whose name is equal to courseName.
So my question consists of two parts:
What is a valid NSPredicate for obtaining all students with a certain course name?
What is a valid NSPredicate for obtaining all courses where a student with a certain firstName is enrolled.
For your reference, here are my entities:
1) Use this to filter an array of students that are in a specific course with courseName :
[NSPredicate predicateWithFormat:#"ANY courses.name like %#", courseName]
Alternatively, if you already have a course object (and name is unique in call course objects), you can simply get all of its students by accessing its students property, i.e.
course.students // returns an NSSet/NSOrderedSet based on your model setup
2) Use this to filter an array of courses that have a student with a certain firstName :
[NSPredicate predicateWithFormat:#"ANY students.firstName like %#", firstName]
Note: NSSet and NSOrderedSet have a method array to get an array of the objects.
See also the docs on NSPredicate under Using Predicates with Key-Paths.
What is a valid NSPredicate for obtaining all students with a certain course name?
[NSPredicate predicateWithFormat:#"name= %#", courseName];
Use above predicate to fetch particular course while has name = courseName;Set that predicate on "Course" entity not "Student" entity.
AFter executing above fetch request, you will have array of courses having that courseName.
Course *course = [fetchedResults objectAtIndex:0];
Now to get all students in that course,
NSArray *students =[course.students allObjects];
now students is an array of "Student" entity objects.
Similarly ,
What is a valid NSPredicate for obtaining all courses where a student with a certain firstName is enrolled.
[NSPredicate predicateWithFormat:#"firstName = %#", name];
Use above predicate to fetch particular student who has name = firstName;Set that predicate on "Student" entity.
AFter executing above fetch request, you will have array of students having that firstName.
Student *student = [fetchedResults objectAtIndex:0];
Now to get all courses for that student,
NSArray *courses =[student.courses allObjects];
now courses is an array of "Course" entity objects.
Hope this helps.

How to use Core Data relationship?

In my app I have 3 related entities. Athlete, Exercise, and Exercise Score.
Athlete has a to-many relationship with Exercise. It's inverse is whosExercise.
Exercise has a to-many relationship with exercise score. It's inverse is whichExercise.
I want to perform a fetch request where I get all the exercise scores for an athlete. How would I get that? Do I need another relationship between Athlete and Exercise Scores, or is that redundant? If it is, how would I use exercise as a predicate for my request?
If Exercise has a relationship to Athlete, then you should be able to do something like:
[NSPredicate predicateWithFormat:#"SELF.athlete = #%", currentAthlete];
and then just:
[Exercise fetchByPredicate:currentAthletePredicate];
Should be it.
If I clearly understand, you need to select all ExerciseScore for Athlete, but Athlete does not have direct relationship with ExerciseScore table.
SLQ Query for this may looks like :
Select *
from ExerciseScore
where IDScore in (select IDScore
from Exercise
where IDExercise in ( select IDExercise
from Athlete
)
)
But in Core Data you can't operate SLQ Queries.
Try this approach.
1.Fetch all Exercise for Athlete:
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Exercise"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.athlete = #%", currentAthlete];
request.predicate=predicate;
NSArray *athleteExercises = [self.managedObjectContext executeFetchRequest:request error:&error];
2.Iterate throughout array of Exercises to get all Exercise score for each Exercise.
NSMutableArray *allScores = [NSMutableArray arrayWithCapacity:0];;
for (Exercise *exercise in athleteExercises) {
if ([exercise.scores count]>0) {
[allScores addObjectsFromArray:[exercise.scores allObjects]; //exercise.scores must be NSSet type
}
}
allScores array now contain All ExerciseScore objects for particular Athlete.
Predicates can have key paths:
#"whichExercise.whosExercise = %#",athlete
Or, if you have the athlete already, don't do a fetch request, just get the scores via the relationship properties.

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* ?

Resources