Relationships
Vasta supports defining relationships between your models. You can define relationships such as hasMany and belongsTo to easily navigate between related models. Related models can be eager loaded or lazy loaded as needed.
One-to-Many Relationships
For example, if you have a Person model and a Pet model, you can define the relationships as follows:
// In Person model
get pets() {
return this.hasMany(Pet, "person_id", "id");
}
// In Pet model
get owner() {
return this.belongsTo(Person, "person_id", "id");
}
You can define multiple relationships between the same models. For example, if you wanted a relationship for a peron's pets, but also a relationship for a person's birds:
// A Person has many Pets
get pets() {
// We pass the Pet class, and the foreign key column on the pets table
return this.hasMany(Pet, "person_id", "id");
}
// A person has many birds, which are a subset of their pets
get birds() {
// we can extend the relationship to get only the person's birds
return this.pets.where("type", "bird");
// Alternatively, we could define the birds relationship directly using hasMany
// return this.hasMany(Pet, "person_id", "id").where("type", "bird");
}
Lazy Loading
You can lazy-load a person's pets or a pet's owner:
const person = await Person.find(1);
const pets = await person.pets; // Get all pets belonging to the person
Lazy loaded relationship queries can be constrained:
const person = await Person.findOrFail(3);
const bird = await person.pets.where("type", "bird").first();
Eager Loading
You can also eager load relationships to optimize your queries:
const person = await Person.with("pets").find(1);
const pets = person.pets; // This will not trigger a new query, as the pets were eager loaded
When eager loading, you can also add custom constraints to the relationship query. Pass an object mapping the relationship name to a callback function receiving the query builder:
// Eager load only pets that are cats, sorting them by name
const person = await Person.with({
pets: (query) => query.where("type", "cat").orderBy("name", "desc"),
}).find(1);
const catsOnly = person.pets;
You can even combine multiple eager loads with and without constraints in the same call:
const people = await Person.with("vehicles", { pets: (query) => query.where("type", "dog") }).get();
Many-to-Many Relationships
You can define many-to-many relationships using the belongsToMany method. This requires a pivot table (or join table) to link the two models together.
For example, if a Pet can visit many Vets, and a Vet can see many Pets, you might use a vet_visits join table. You can define the relationship on the Pet model like this:
// In Pet model
get vets() {
// belongsToMany(RelatedModel, pivotTable, foreignPivotKey, relatedPivotKey)
return this.belongsToMany(Vet, "vet_visits", "pet_id", "vet_id");
}
You can interact with a many-to-many relationship just like any other, choosing to either eager load or lazy load the related models:
// Lazy loading
const pet = await Pet.find(1);
const vets = await pet.vets; // Get all vets this pet has visited
// Eager loading
const pets = await Pet.with("vets").get();
const firstPetVets = pets[0].vets;