-
Notifications
You must be signed in to change notification settings - Fork 0
One to many relationships where the children are stored in a separate collection from the parent record are defined using Tower's hasMany
and belongsTo
macros. This exhibits similar behavior to Rails' Active Record.
The parent record of the relation should use the hasMany
macro to indicate is has n number of referenced children, where the record that is referenced uses belongsTo.
class App.User extends Tower.Model
@hasMany "posts"
class App.Post extends Tower.Model
@field "title", type: "String"
@belongsTo "user"
Definitions are required on both sides to the relation in order for it to work properly.
Accessing the relations is handled through the methods created based on the names of the relations. The following example shows basic access on both sides of the relation.
# Return the child posts.
user.posts().all()
# Set the child posts.
user.posts().set([new App.Post])
# Return the parent user.
post.user()
# Set the parent user.
post.user().create(new App.User)
From the parent side, records in the referenced child can be appended to using traditional array syntax or the special association proxy methods. On the child side the only option is to replace the existing with a newly built or created record.
# Append one or many child posts, saving them if the user is persisted.
user.posts().create(new Post)
# Appends and returns a new child post from the attirbutes.
user.posts().build(title: "Berlin never sleeps.")
# Appends, saves, and returns a new child post from the attirbutes.
user.posts().create(title: "Berlin is far cooler than New York.")
# Replace the parent with a new one from the attributes.
post.buildUser(title: "Prince")
# Replace the parent with a newly saved one from the attributes.
post.createUser(title: "Prince")
Records in the referenced many can be removed in several different manners, either through the relation, criteria, or accessors.
# Delete all referenced records
user.posts().destroy()
# Delete all matching referenced records.
user.posts().where(title: "Berlin").destroy()
# Delete the parent referenced record.
post.user().destroy()
Finding records in the referenced children is handled through find or by using chained criteria on the relation.
# Find a child by a single or multiple ids.
user.posts().find(id)
user.posts().find([ idOne, idTwo ])
# Find matching referenced children.
user.posts().where(title: "Berlin")
# Do any children exist that are persisted?
user.posts().exists()
When a child referenced record can belong to more than one type of parent record, you can tell Tower to support this by adding the as option to the definition on the parents, and the polymorphic option on the child.
class Company extends Tower.Model
@hasMany "posts", as: "postable"
class User extends Tower.Model
@hasMany "posts", as: "postable"
class Post extends Tower.Model
@belongsTo "postable", polymorphic: true
You can tell Tower what to do with child relations of a has many when unsetting the relation via the dependent option. This also applies to calling #delete on the relation. The valid options are:
-
"delete"
: Delete the child records. -
"destroy"
: Destroy the child records. -
"nullify"
: Orphan the child records.
class Company extends Tower.Model
@hasMany "posts", as: "postable", dependent: "delete"
class User extends Tower.Model
@hasMany "posts", as: "postable", dependent: "nullify"
# Delete all the child relations:
company.posts().set([])
company.posts().set(null)
company.posts().clear()
company.posts().delete()
# Orphan all the child relations:
user.posts().delete()
# Delete a single child relation:
company.posts().delete(post)
# Orphan a single child relation:
user.posts().delete(post)
If the dependent option is not defined, the default is to nullify.
The database-specific ways the data is stored is recorded in the Tower.Store
section. Below we define how associations are stored in general; i.e. in MongoDB the id
is actually saved in an _id
field and defaults to a MongoDB specific object, so a MongoDB record with an id
would look like this: { "_id" : ObjectId("4d2ed089fb60ab534684b7e9") }
.
When defining a relation of this nature, each record is stored in it's respective collection, but the child record contains a "foreign key" reference to the parent.
# The parent `user` record.
# user.save()
{ "id" : 123 }
# The child `post` record.
# user.posts().create()
{
"id" : 987,
"userId" : 123,
"title": "A Post!"
}
Records that are embedded using the embedsMany macro are stored as an array of hashes inside the parent in the parent's database collection.
# users collection
{
"id" : 123,
"posts" : [
{
"id" : 987,
"title": "A Post!"
}
]
}
Sometimes you don't want to embed a record inside another, but you want a quick way to query the associated records. This is when you use the cache
option, which stores the ids of the associated record in an array on the parent record.
# users collection
{
"id" : 123,
"postIds" : [987]
}
# posts collection
{
"id" : 987,
"userId" : 123,
"title": "A Post!"
}