A one-to-one relationship means one record in a table is associated with exactly one record in another table. Think of it like a person and their passport - each person has one passport, and each passport belongs to one person.
// User modelclass User extends Model { profile() { return this.hasOne('Profile', 'user_id'); }}// Profile model class Profile extends Model { user() { return this.belongsTo('User', 'user_id'); }}// Usageconst user = await User.with('profile').find(1);console.log(user.profile.bio);const profile = await Profile.with('user').find(1);console.log(profile.user.name);
String References: Always use string model names ('Profile') instead of importing classes to avoid circular dependencies.
A one-to-many relationship means one record can be associated with multiple records in another table. Think of it like a blog author and their posts - one author can write many posts, but each post belongs to only one author.
export default class CreatePostsTable { async up(schema) { await schema.createTable('posts', (table) => { table.increments('id'); table.string('title').notNullable(); table.text('content').notNullable(); table.integer('user_id').unsigned().notNullable(); table.boolean('is_published').defaultTo(false); table.timestamps(true, true); table.foreign('user_id').references('id').inTable('users').onDelete('CASCADE'); table.index('user_id'); // Index for better query performance }); }}
Model definitions:
Copy
Ask AI
// User modelclass User extends Model { posts() { return this.hasMany('Post', 'user_id'); }}// Post modelclass Post extends Model { author() { return this.belongsTo('User', 'user_id'); }}// Usageconst user = await User.with('posts').find(1);console.log(`${user.name} has ${user.posts.length} posts`);const post = await Post.with('author').find(1);console.log(`Written by: ${post.author.name}`);
A many-to-many relationship means multiple records in one table can be associated with multiple records in another table. Think of it like blog posts and tags - one post can have many tags, and one tag can be used on many posts. This requires a “pivot” or “junction” table to store the connections.
A polymorphic relationship allows one model to belong to multiple other model types. Think of it like comments that can be added to both blog posts and videos - the comment doesn’t care what type of content it’s attached to, it just knows it’s “commentable”.
A has-many-through relationship provides a shortcut to access distant relationships through an intermediate model. Think of it like getting all posts from a specific country - you go through users to get to their posts, but you want to skip the middle step.
// Country modelclass Country extends Model { users() { return this.hasMany('User', 'country_id'); } // Get all posts from users in this country posts() { return this.hasManyThrough('Post', 'User', 'country_id', 'user_id'); }}// User modelclass User extends Model { country() { return this.belongsTo('Country', 'country_id'); } posts() { return this.hasMany('Post', 'user_id'); }}// Post modelclass Post extends Model { author() { return this.belongsTo('User', 'user_id'); }}// Usageconst country = await Country.with('posts').find(1);console.log(`${country.name} has ${country.posts.length} posts`);// Get posts with author informationconst countryWithPosts = await Country.with('posts.author').find(1);
// Users who have postsconst authorsWithPosts = await User.has('posts').get();// Users with more than 5 postsconst prolificAuthors = await User.has('posts', '>', 5).get();// Users with published postsconst publishedAuthors = await User.whereHas('posts', (query) => { query.where('is_published', true);}).get();
// ❌ Loads all posts just to countconst user = await User.with('posts').find(1);const postCount = user.posts.length;// ✅ Just countsconst user = await User.find(1);const postCount = await user.posts().count();