Skip to content
Lance Pollard edited this page Apr 12, 2012 · 1 revision

One-to-Many Relationships

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.

Defining

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.

Accessors

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)

Building and Creating

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

Removal

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

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

Polymorphic Behavior

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

Dependent Behavior

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.

Storage

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") }.

Referenced

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!"
}

Embedded

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

Cached

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!"
}

Tower.js

Everything here will be reflected on http://towerjs.org/guides.

Clone this wiki locally