Core Data Equivalent of Rails :has_and_belongs_to_many =>, :through =>
-
13-09-2020 - |
Question
I have a Rails background and am trying to muck through Core Data. How does Core Data handle complex :has_and_belongs_to_many =>, :through =>
relationships?
As an example, suppose you were modeling open source project contributions. You might set up a table structure such as:
Contributor Contribution Project
---------------- ------------------ ------------------
name typeOfContribution name
address amountOfContribution urlForSourceCode
... startedOnDate ...
... endedOnDate ...
contributor
project
In this case you would say that both Contributor and Project :has_and_belong_to_many
of each other, and that both relationships are :through
the Contribution join table. This lets you store information about the relationship within the relationship itself (the Contribution table).
The Rails convenience methods are also really handy to have, so you can navigate through the relationships simply. For example:
contributor.projects // this would return an array of project objects associated with the contributor
project.contributors // this would return an array of contributors associated with a project
So my question boils do to this:
- Is it possible to model in a similar way using Core Data? and
- If so, can the relationships be traversed as easily as the code above (sample code would be great)?
Solution
In Core Data, you need to model the relationships "crossing" the intermediate entity yourself, the framework won't do this for you. It doesn't really have the concept of "has many through" like ActiveRecord in Rails or "flattened relationships" like in the Enterprise Objects Framework.
That said, you can implement properties and methods for them easily. For example:
@interface Contributor : NSManagedObject
@property (readonly, copy) NSSet *projects;
@end
@implementation Contributor
- (NSSet *)projects {
// use aggregate KVC to do the iteration automatically
return [self.contributions valueForKey:@"projects"];
}
@end
If you need to make your "projects" relationship modifiable it should be similar. Making it support Key-Value Observing, say if you need to bind to it, is more interesting - you essentially need to have your Contributor observe every one of its Contributions' projects relationships, and post similar KVO notifications for its own "projects" relationship. Not that hard, just a bit of boilerplate code to write.
OTHER TIPS
Actually, they're both pretty much same because both are object graph systems. The vocabulary is different. Rails still maintains much of the relational database nomenclature whereas Core Data uses a purely object oriented nomenclature.
Instead of "tables" core data has "entities" that represent attributes and relationships of runtime objects. Core Data names relationships at each end of relationship using to-one(-->), to-many(-->>).
So, in your example you would have (pseudocode):
Contributor{
name:string;
address:string;
contributions<-->>Contribution.contributor;
}
Contribution{
typeOfContribution:string;
...
contributor<<-->Contributor.contributions;
}
Project{
name:string;
...
contributions<-->Contribution.project;
}
To find a project starting with a Contributor
, you would walk the relationship using a keypath in a predicate. Something like:
NSPredicate *aPred=[NSPredicate predicateWithFormat:@"project.name=%@",nameVariable];
NSSet *foundProjects=[aContributorObj.contributions filteredSetUsingPredicate:aPred];
This would take all the Contribution
objects related to the specific Contributor
object and then walk the keypath to the Contribution
object's project relationship and then to the project objects name attribute. If the name
attribute matches the value in nameVariable
the project object is returned to the set held as foundProjects
.
I'm not a big Rails guy but from what I have done it looks like Core Data and Rails are fairly close in functionality, its just the nomenclature that is different. Core Data has always had the graphical data modeler and predicates so it uses fewer textual shortcuts than does Rails.
Have a look
// this would return an array of project objects associated with the contributor
contributor.projects
// this would return an array of contributors associated with a project
project.contributors
Class Contributor < ActiveRecord::Base
has_and_belongs_to_many :projects , :join_table => "contributions"
end
Class Contribution < ActiveRecord::Base
end
Class Project < ActiveRecord::Base
has_and_belongs_to_many :contributors , :join_table => "contributions"
end