Skip to content

HasMany Relationship

The HasMany relationship represents a one-to-many relationship where the foreign model stores a reference to the current model.

Explanation

For example, a User has many Posts:

Defining the Relationship

The relationship is defined in your model configuration under the relationships property as a tuple:

typescript
[HasMany, foreignModelTable: string, foreignKey: string, originatingPrimaryKey?: string, foreignPrimaryKey?: string]

Required Parameters

  • HasMany: The relationship type
  • foreignModelTable: The table name of the related model
  • foreignKey: The foreign key in the related model that references the current model

Optional Parameters

  • originatingPrimaryKey: The primary key in the current model (defaults to 'id')
  • foreignPrimaryKey: The primary key in the foreign model (defaults to 'id')

Example

typescript
{
  models: {
    users: {
      schema: '++id, email, createdAt, updatedAt',
      properties: ['id', 'email', 'password'],
      primaryKey: 'id',
      relationships: {
        posts: [HasMany, 'posts', 'user_id'], 
      }
    },
    posts: {
      schema: '++id, user_id, title, body, created_at', 
      properties: ['id', 'user_id', 'title', 'body', 'created_at'],
    }
  }
}

In this example:

  • The Post model has a user_id foreign key that references the User model's id
  • The relationship is defined as [HasMany, 'posts', 'user_id'] where:
    • 'posts' is the foreignModelTable
    • 'user_id' is the foreignKey
    • The primary keys default to 'id' since they're not specified

Accessing the Relationship

typescript
// Get all posts for a user
const user = await User.find(1)
const posts = await user.posts 

// The relationship is reactive for both individual posts and the collection
posts.forEach(post => { 
  post.onChange((updatedPost) => {
    console.log('Post updated:', updatedPost)
  })
})

user.onPropertyChange('posts', (newPosts, oldPosts) => { 
  console.log('Posts collection changed:', {
    new: newPosts,
    old: oldPosts
  })
})

// You can also eager load the relationship
const users = await User.query()
  .with('posts') 
  .fetch()

Working with Collections

HasMany relationships return an array of model instances that you can manipulate using standard array methods:

typescript
// Filter posts
const publishedPosts = user.posts.filter(post => post.published) 

// Sort posts by date
const sortedPosts = user.posts.sort((a, b) =>
  new Date(b.created_at) - new Date(a.created_at)
)

// Map to get just the titles
const postTitles = user.posts.map(post => post.title) 

// Count posts
const postCount = user.posts.length

You can also use the query builder to fetch related records with conditions:

typescript
// Get users with their published posts
const users = await User.query()
  .with('posts') 
  .where(user => user.posts.some(post => post.published)) 
  .fetch()