# Objection Relationships

# Introduction

Database tables are often related to one another. A relationship defines how two entities relate to each other. In a relational database, this is represented by a foreign key constraint.

  • One To One (BelongsToOneRelation)
  • One To Many (HasManyRelation)
  • Many To Many (ManyToManyRelation)

While relations are usually created between the primary key of one table and a foreign key reference of another table, ExpressWebJs has no such limitations. You can create relationship using any two columns (or any sets of columns). You can even create relation using values nested deep inside json columns.

# One To One (BelongsToOneRelation)

BelongsToOneRelation: Use this relation when the source model has the foreign key

import { Model } from "Elucidate/Database/Model";

export class ProfileModel extends Model {
  static tableName = "profiles";

  id!: number;
  gender!: string;
  photo!: string;
  user_id!: number;
}
import { Model } from "Elucidate/Database/Model";
import { ProfileModel } from "App/Model/ProfileModel";

export class UserModel extends Model {
  static tableName = "users";

  id!: number;
  first_name!: string;
  last_name!: string;
  phone_number!: string;
  email!: string;
  password!: string;

  static relationMappings = {
    profile: {
      relation: Model.BelongsToOneRelation,
      modelClass: ProfileModel,
      join: {
        from: "users.id",
        to: "profiles.user_id",
      },
    },
  };
}

Or you can use the belongsTo method.

import { Model } from "Elucidate/Database/Model";

export class UserModel extends Model {
  static tableName = "users";

  id!: number;
  first_name!: string;
  last_name!: string;
  phone_number!: string;
  email!: string;
  password!: string;

  static relationMappings() {
    return {
      profile: this.belongsTo("App/Model/ProfileModel", { foreign_key: "user_id" }),
    };
  }
}

# One To Many (HasManyRelation)

HasManyRelation: Use this relation when the related model has the foreign key

import { Model } from "Elucidate/Database/Model";
import { CarModel } from "App/Model/CarModel";

export class PersonModel extends Model {
  static tableName = "persons";

  static relationMappings = {
    cars: {
      relation: Model.HasManyRelation,
      modelClass: CarModel,
      join: {
        from: "persons.id",
        to: "cars.owner_id",
      },
    },
  };
}

Or you can use the hasMany method.

import { Model } from "Elucidate/Database/Model";

export class PersonModel extends Model {
  static tableName = "persons";

  static relationMappings() {
    return {
      cars: this.hasMany("App/Model/CarModel", { foreign_key: "owner_id" }),
    };
  }
}

HasOneRelation: Just like HasManyRelation but for one related row

import { Model } from "Elucidate/Database/Model";
import { CarModel } from "App/Model/CarModel";

export class PersonModel extends Model {
  static tableName = "persons";

  static relationMappings = {
    car: {
      relation: Model.HasOneRelation,
      modelClass: CarModel,
      join: {
        from: "persons.id",
        to: "cars.owner_id",
      },
    },
  };
}

Or you can use the hasOne method.

import { Model } from "Elucidate/Database/Model";

export class PersonModel extends Model {
  static tableName = "persons";

  static relationMappings() {
    return {
      cars: this.hasOne("App/Model/CarModel", { foreign_key: "owner_id" }),
    };
  }
}

# Many To Many (ManyToManyRelation)

ManyToManyRelation: Use this relation when the model is related to a list of other models through a join table

import { Model } from "Elucidate/Database/Model";
import { MovieModel } from "App/Model/MovieModel";

export class PersonModel extends Model {
  static tableName = "persons";

  static relationMappings = {
    movies: {
      relation: Model.ManyToManyRelation,
      modelClass: MovieModel,
      join: {
        from: "persons.id",
        through: {
          // persons_movies is the join table.
          from: "persons_movies.personId",
          to: "persons_movies.movieId",
        },
        to: "movies.id",
      },
    },
  };
}

Or you can use the hasManyThrough method.

import { Model } from "Elucidate/Database/Model";

export class PersonModel extends Model {
  static tableName = "persons";

  static relationMappings() {
    return {
      movie: this.hasManyThrough("App/Model/MovieModel", "App/Model/PersonsMoviesModel", {
        join_parient_foreign_key: "movie_id",
        join_final_foreign_key: "person_id",
      }),
    };
  }
}

HasOneThroughRelation: Use this relation when the model is related to a single model through a join table

import { Model } from "Elucidate/Database/Model";
import MovieModel from "App/Model/MovieModel";

export class PersonModel extends Model {
  static tableName = "persons";

  static relationMappings = {
    movies: {
      relation: Model.HasOneThroughRelation,
      modelClass: MovieModel,
      join: {
        from: "persons.id",
        through: {
          // persons_movies is the join table.
          from: "persons_movies.personId",
          to: "persons_movies.movieId",
        },
        to: "movies.id",
      },
    },
  };
}

Or you can use the hasOneThrough method.

import { Model } from "Elucidate/Database/Model";

export class PersonModel extends Model {
  static tableName = "persons";

  static relationMappings() {
    return {
      movie: this.hasOneThrough("App/Model/MovieModel", "App/Model/PersonsMoviesModel", {
        join_parient_foreign_key: "movie_id",
        join_final_foreign_key: "person_id",
      }),
    };
  }
}

Visit ObjectionJs documentation site (opens new window) for more info.

# Add ons

There are other ways to handle Objection relationships in ExpressWebJs

Relationship mapping method now has a nice way of handling relationships with the new hasMany, belongsTo, hasOne, hasOneThrough, hasManyThrough methods which also accepts addition options.

# hasManyThrough()

export class UserModel extends Model {
  static tableName = "users";

  id!: string;
  name!: string;
  email!: string;
  password!: string;

  static relationMappings() {
    return {
      carOwner: this.hasManyThrough("App/Model/Cars_model", "App/Model/CarOwners_model", {
        join_final_foreign_key: "user_id",
        join_parent_foreign_key: "car_id",
      }),
    };
  }
}

# hasOneThrough()

export class UserModel extends Model {
  static tableName = "users";

  id!: string;
  name!: string;
  email!: string;
  password!: string;

  static relationMappings() {
    return {
      carOwner: this.hasOneThrough("App/Model/Cars_model", "App/Model/CarOwners_model", {
        join_final_foreign_key: "user_id",
        join_parent_foreign_key: "car_id",
      }),
    };
  }
}

# hasOne()

export class UserModel extends Model {
  static tableName = "users";

  id!: string;
  first_name!: string;
  last_name!: string;
  email!: string;
  password!: string;

  static relationMappings() {
    return {
      profile: this.hasOne("App/Model/Profile_model"),
    };
  }
}

# hasMany()

export class UserModel extends Model {
  static tableName = "users";

  id!: string;
  first_name!: string;
  last_name!: string;
  email!: string;
  password!: string;

  static relationMappings() {
    return {
      profile: this.hasMany("App/Model/Cars_model"),
    };
  }
}

# Relation filters

You can also add some filters in your relationship mapping:

  static relationMappings() {
    return {
      profile: this.hasMany("App/Model/Cars_model",{
        filter: (builder) => {
          builder.where("name", "Mercedes");
        },
      }),
    };
  }

# belongsTo()

export class Posts extends Model {
  static tableName = "posts";

  id!: string;
  name!: string;
  user_id!: string;

  static relationMappings() {
    return {
      users: this.belongsTo("App/Model/Users_model"),
    };
  }
}