LLaraNode
Database Package

Relationships

LaraNode models support Laravel-style relationships for connecting related data.

Relationships

LaraNode models support Laravel-style relationships for connecting related data.

Defining Relationships

Define relationships in the static relationships property:

class User extends Model {
  static table = "users";

  static relationships = {
    posts: {
      type: "hasMany",
      model: () => Post,
      foreignKey: "user_id",
    },
    profile: {
      type: "hasOne",
      model: () => Profile,
      foreignKey: "user_id",
    },
    role: {
      type: "belongsTo",
      model: () => Role,
      foreignKey: "role_id",
    },
  };
}

Has One

One-to-one relationship:

class User extends Model {
  static relationships = {
    profile: {
      type: "hasOne",
      model: () => Profile,
      foreignKey: "user_id",
    },
  };
}

// Access
const user = await User.find(1);
const profile = await user.profile;

Has Many

One-to-many relationship:

class User extends Model {
  static relationships = {
    posts: {
      type: "hasMany",
      model: () => Post,
      foreignKey: "user_id",
    },
  };
}

// Access
const posts = await user.posts;

Belongs To

Inverse of hasOne/hasMany:

class Post extends Model {
  static relationships = {
    author: {
      type: "belongsTo",
      model: () => User,
      foreignKey: "user_id",
    },
  };
}

// Access
const author = await post.author;

Belongs To Many

Many-to-many relationship with pivot table:

class User extends Model {
  static relationships = {
    roles: {
      type: "belongsToMany",
      model: () => Role,
      pivotTable: "role_user",
      foreignKey: "user_id",
      relatedKey: "role_id",
    },
  };
}

// Access
const roles = await user.roles;

// With pivot data
for (const role of roles) {
  console.log(role.pivot.created_at);
}

Has One Through

class User extends Model {
  static relationships = {
    phone: {
      type: "hasOneThrough",
      model: () => Phone,
      through: () => Profile,
      firstKey: "user_id",
      secondKey: "profile_id",
    },
  };
}

Has Many Through

class Country extends Model {
  static relationships = {
    posts: {
      type: "hasManyThrough",
      model: () => Post,
      through: () => User,
      firstKey: "country_id",
      secondKey: "user_id",
    },
  };
}

Eager Loading

Load relationships to avoid N+1 queries:

// Load single relationship
const users = await User.with("posts").get();

// Load multiple
const users = await User.with("posts", "profile", "roles").get();

// Constrained loading
const users = await User.with({
  posts: (query) => query.where("published", true).limit(5),
}).get();

Querying Relationships

// Where has relationship
const users = await User.whereHas("posts", (query) => {
  query.where("published", true);
}).get();

// Where doesnt have
const users = await User.whereDoesntHave("posts").get();

Complete Example

class User extends Model {
  static table = "users";
  static fillable = ["name", "email"];

  static relationships = {
    posts: {
      type: "hasMany",
      model: () => Post,
      foreignKey: "user_id",
    },
    profile: {
      type: "hasOne",
      model: () => Profile,
      foreignKey: "user_id",
    },
    roles: {
      type: "belongsToMany",
      model: () => Role,
      pivotTable: "role_user",
      foreignKey: "user_id",
      relatedKey: "role_id",
    },
  };
}

class Post extends Model {
  static table = "posts";
  static fillable = ["title", "content", "user_id"];

  static relationships = {
    author: {
      type: "belongsTo",
      model: () => User,
      foreignKey: "user_id",
    },
  };
}

// Usage
const user = await User.with("posts", "profile", "roles").find(1);
console.log(user.posts.length);
console.log(user.profile.bio);
console.log(user.roles.map((r) => r.name));

Next Steps