-
Notifications
You must be signed in to change notification settings - Fork 2
Relationships
####Relationship
Relationship is a generic struct used to declare relationships between models that conform to SQLiteModel. Its initialization is similar to Expression<T> in that it takes a string value which is used to build the schema.
static let myRelationship = Relationship<OtherModel>("other_model")SQLiteModel supports:
- Single relationships
Relationship<T> - Optional single relationships
Relationship<T?> - Multiple relationships
Relationship<[T]>
Relationships can also be marked as unique.
static let myUniqueRelationship = Relationship<OtherModel>("other_model", unique: true)Relationships must be added in the buildTable method of the model:
static let myRelationship = Relationship<OtherModel>("other_model")
static func buildTable(tableBuilder: TableBuilder) {
...
tableBuilder.relationship(myRelationship, mappedFrom: self)
...
}The mappedFrom parameter exists to declare the second half of the mapping. Most of the time you will want to map from the models type, but there are times when you may want to hold a reference to a relationship between 2 other models.
####Counts
You can get the count of any multiple relationship with the instance method countForRelationship.
struct Person {
...
static let siblings = Relationship<[Person]>("siblings")
...
}
let somePerson = Person.fetchAll().first
let siblingCount = somePerson?.countForRelationship(Person.siblings)###Putting it all together
To show examples of this, lets build off our Movie model.
struct Movie: SQLiteModel {
var localID: SQLiteModelID = -1
static let Title = Expression<String>("title")
static let ReleaseDate = Expression<NSDate>("release_date")
static let Gross = Expression<Double>("gross")
// Relationships
static let Director = Relationship<Person>("director")
// A movie can have a prequel, but it doesn't need to have one. If it has one, that prequel is unique (2 movies cant have the same prequel)
// Operating off the assumption that Star Wars IV is the prequel of Empire Strikes Back, but not Return of the Jedi
static let Prequel = Relationship<Movie?>("prequel", unique: true)
static let Actors = Relationship<[Person]>("actors")
static func buildTable(tableBuilder: TableBuilder) {
tableBuilder.column(Title)
tableBuilder.column(ReleaseDate)
tableBuilder.column(Gross, defaultValue: 0)
// Relationships
tableBuilder.relationship(Director, mappedFrom: self)
tableBuilder.relationship(Prequel, mappedFrom: self)
tableBuilder.relationship(Actors, mappedFrom: self)
}
}Relationships follow the same getter and setter pattern as normal value types. If you want to include relationships when you create a new instance, add them with the relationshipSetters parameter in the new method. Update methods also take relationship setters.
let georgeLucas = try Person.new([Person.Name <- "George Lucas"])
let luke = try Person.new([Person.Name <- "Mark Hammel"])
let leah = try Person.new([Person.Name <- "Carrie Fisher"])
let yoda = try Person.new([Person.Name <- "Yoda"])
let aNewHope = try Movie.new([
Movie.Title <- "Episode IV: A new Hope",
], relationshipSetters: [
Movie.Director <- georgeLucas,
Movie.Actors <- [luke, leah],
])
let empireStrikesBack = try Movie.new([
Movie.Title <- "Episode V: The Emprire Strikes Back",
], relationshipSetters: [
Movie.Director <- georgeLucas,
Movie.Actors <- [luke, leah, yoda],
])
// Set
aNewHope.set(Movie.Prequel, value: empireStrikesBack)
// is the same as
aNewHope <| Movie.Prequel |> empireStrikesBack
// Get
let actors = empireStrikesBack.get(Movie.Actors)
// is the same as
actors = empireStrikesBack => Movie.ActorsUnlike normal value types, relationship sets and gets are not O(1) operations. while they are optimized using caches, there is still some overhead to the operations. because of this, SQLiteModel supplies the setInBackground and getInBackground methods. We also supply custom operators for these methods, the common element being a *.
#####Get in background:
empireStrikesBack.getInBackground(Movie.Actors, completion: { actors in
print(actors)
})
// is the same as
empireStrikesBack ~* Movie.Actors ~* { actors in
print(actors)
}
// is the same as
empireStrikesBack ~* (Movie.Actors, { actors in
print(actors)
})#####Set in background:
let actors = [luke, leah, yoda]
empireStrikesBack.setInBackground(Movie.Actors, value: actors)
// is the same as
empireStrikesBack <| Movie.Actors |* actorsSQLiteModel