Skip to content

Relationships

ReActive Record provides several types of relationships to define connections between your models. These relationships are reactive, meaning changes to related models will automatically update across your application.

Types of Relationships

One-to-One Relationships

  • HasOne: A model has exactly one instance of another model
  • BelongsTo: The inverse of HasOne, where a model belongs to another model

One-to-Many Relationships

  • HasMany: A model has multiple instances of another model
  • HasManyThrough: A model has multiple instances of another model through an intermediate model

Many-to-Many Relationships

  • ManyToMany: Models that can have multiple instances of each other

Polymorphic Relationships

  • MorphMany: A model has multiple instances of multiple possible model types
  • MorphOne: A model has one instance of multiple possible model types
  • MorphTo: The inverse of MorphOne/MorphMany

Common Features

Relationship Loading

Relationships are lazy-loaded by default. You can load them in several ways:

Manual Loading

typescript
// Load a single relationship
const user = await User.find(1)
await user.load('profile')

// Load multiple relationships
await user.loadMany(['profile', 'posts'])

Eager Loading with Query Builder

typescript
// Load all users with their profiles
const users = await User.query()
  .with('profile')
  .fetch()

// Load multiple relationships
const users = await User.query()
  .with('profile', 'posts')
  .fetch()

// Load all defined relationships
const users = await User.query()
  .withAll()
  .fetch()

Reactivity

All relationships in ReActive Record are reactive, meaning:

  1. Changes to related models are automatically reflected
  2. You can listen to changes on related models
  3. Changes are propagated across the application

Example of reactive relationships:

typescript
const user = await User.find(1)
await user.load('profile')

// Listen to changes on the profile
user.onPropertyChange('profile', (newProfile, oldProfile) => {
  console.log('Profile changed:', { new: newProfile, old: oldProfile })
})

// Listen to specific property changes on the profile
user.profile.onPropertyChange('full_name', (newName, oldName) => {
  console.log('Name changed:', { new: newName, old: oldName })
})

Important Notes

  1. Relationships are lazy-loaded by default
  2. Use eager loading (with()) when you know you'll need related models
  3. Relationship changes are reactive and will update automatically
  4. Changes to related models trigger appropriate events on the parent model
  5. You can chain relationship loading with other query builder methods

See the individual relationship documentation for specific details about each type of relationship.