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:

  1. Is it possible to model in a similar way using Core Data? and
  2. If so, can the relationships be traversed as easily as the code above (sample code would be great)?
Was it helpful?

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top