1- One-to-One Relationship in Laravel Eloquent
A One-to-One relationship means that a record in one table is associated with exactly one record in another table.
Real-world examples:
-
A User has one Profile.
-
A Company has one Address.
-
A Car has one Engine.
In Laravel, One-to-One relationships are defined using the hasOne and belongsTo methods.
When to Use One-to-One?
Use One-to-One when:
-
Each entity has an additional set of details stored in a separate table.
-
You want to keep your tables organized and normalized instead of having one huge table.
📌 Example:
Instead of putting user profile details (bio, address, phone) in the users table, we create a separate profiles table.
Database Structure for One-to-One
| users table | profiles table |
|---|---|
| id (Primary Key) | id (Primary Key) |
| name | bio |
| phone | |
| ... | user_id (Foreign Key → users.id) |
Here:
-
profiles.user_idis a foreign key referencingusers.id. -
Each profile belongs to exactly one user.
Step 1: Create Migrations
// users migration (already exists in most projects)
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
// profiles migration
Schema::create('profiles', function (Blueprint $table) {
$table->id();
$table->text('bio')->nullable();
$table->string('phone')->nullable();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
Here:
-
user_idlinks the profile to the user. -
onDelete('cascade')ensures the profile is deleted if the user is deleted.
Step 2: Define Models
User Model (User.php)
class User extends Model
{
use HasFactory;
public function profile()
{
return $this->hasOne(Profile::class);
}
}
Profile Model (Profile.php)
class Profile extends Model
{
use HasFactory;
public function user()
{
return $this->belongsTo(User::class);
}
}
Step 3: Querying One-to-One Relationship
1. Get a User’s Profile
$user = User::find(1);
$profile = $user->profile;
2. Get Profile’s User
$profile = Profile::find(1);$user = $profile->user;
3. Create a Profile for a User
$user = User::find(1);
$user->profile()->create(
[
'bio' => 'I am a Laravel Developer.',
'phone' => '123-456-7890',
]
);
4. Update Profile
$user->profile()->update([
'bio' => 'I now specialize in Laravel Eloquent Relationships!',
]);
5. Eager Loading (Optimize Queries)
$users = User::with('profile')->get();
foreach ($users as $user) {
echo $user->name . ' - ' . $user->profile->phone;
}
Real-World Example: User & Profile
Imagine a social networking site:
-
userstable stores login information. -
profilestable stores personal details like bio, address, and social links.
This design:
✅ Keeps data organized.
✅ Makes queries faster.
✅ Avoids a bloated users table.
Advantages of One-to-One Relationship in Laravel
-
Better organization: Separate concerns into different tables.
-
Easier updates: Modify profile details without touching the
userstable. -
Scalability: You can add more profile fields later without changing the
userstable.
2- One-to-Many Relationship in Laravel Eloquent
A One-to-Many relationship means that one record in a table can have multiple related records in another table.
Real-world examples:
-
A User has many Posts.
-
A Category has many Products.
-
An Order has many Order Items.
In Laravel, this is defined using the hasMany and belongsTo methods.
When to Use One-to-Many?
Use One-to-Many when:
-
You want to represent ownership (e.g., a user owns posts).
-
A single parent can have multiple children.
-
You want to keep related data in separate tables for normalization.
Database Structure for One-to-Many
| users table | posts table |
|---|---|
| id (PK) | id (PK) |
| name | title |
| body | |
| ... | user_id (FK → users.id) |
Here:
-
users.id= primary key. -
posts.user_id= foreign key referencingusers.id.
Step 1: Create Migrations
// users migration (already exists)
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
// posts migration
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('body');
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
👉 user_id links each post to its author (user).
Step 2: Define Models
User Model (User.php)
class User extends Model
{
use HasFactory;
public function posts()
{
return $this->hasMany(Post::class);
}
}
Post Model (Post.php)
class Post extends Model
{
use HasFactory;
public function user()
{
return $this->belongsTo(User::class);
}
}
Step 3: Querying One-to-Many Relationship
i. Get All Posts of a User
$user = User::find(1);
$posts = $user->posts;
foreach ($posts as $post) {
echo $post->title;
}
ii. Get the Author of a Post
$post = Post::find(1);
$author = $post->user;
echo $author->name;
iii. Create a New Post for a User
$user = User::find(1);
$user->posts()->create([
'title' => 'My First Laravel Post',
'body' => 'This is an example of One-to-Many relationship in Laravel.',
]);
iv. Eager Loading (Avoid N+1 Problem)
$users = User::with('posts')->get();
foreach ($users as $user) {
echo $user->name . ' has ' . $user->posts->count() . ' posts.';
}
v. Filtering Related Models
$users = User::with(['posts' => function ($query) {
$query->where('status', 'published');
}])->get();
Real-World Example: Blog Application
In a Blog System:
-
Each User writes multiple Posts.
-
Each Post belongs to one User.
This makes it easy to:
✅ Get all posts written by a specific user.
✅ Find the author of any post.
✅ Count how many posts a user has written.
Advantages of One-to-Many in Laravel
-
Simplicity: Easy to define and query.
-
Performance: Works with eager loading and
withCount(). -
Real-world mapping: Matches common scenarios like users/posts, categories/products, orders/items.
3- Many-to-Many Relationship
A Many-to-Many relationship means that records in one table can relate to multiple records in another table, and vice versa.
👉 Real-world examples:
-
A Student can enroll in many Courses, and a Course can have many Students.
-
A User can have many Roles, and a Role can belong to many Users.
-
A Product can belong to many Categories, and a Category can contain many Products.
In Laravel, this is defined using the belongsToMany method.
When to Use Many-to-Many?
Use Many-to-Many when:
-
You need a two-way relationship.
-
Both entities can be linked to multiple records.
-
You want to store extra information in the relationship (e.g., enrollment date, role type).
Database Structure for Many-to-Many
Many-to-Many relationships require a pivot table.
For example:
| students table | courses table | course_student (pivot table) |
|---|---|---|
| id (PK) | id (PK) | id (PK) |
| name | title | student_id (FK → students.id) |
| description | course_id (FK → courses.id) | |
| ... | ... | created_at / updated_at |
📌 The pivot table (course_student) holds the relationship between students and courses.
Step 1: Create Migrations
// students migration
Schema::create('students', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
// courses migration
Schema::create('courses', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->timestamps();
});
// pivot table migration (course_student)
Schema::create('course_student', function (Blueprint $table) {
$table->id();
$table->foreignId('student_id')->constrained()->onDelete('cascade');
$table->foreignId('course_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
The pivot table course_student connects students and courses.
Step 2: Define Models
Student Model (Student.php)
class Student extends Model
{
use HasFactory;
public function courses()
{
return $this->belongsToMany(Course::class);
}
}
Course Model (Course.php)
class Course extends Model
{
use HasFactory;
public function students()
{
return $this->belongsToMany(Student::class);
}
}
Step 3: Querying Many-to-Many Relationship
1. Get All Courses of a Student
$student = Student::find(1);
$courses = $student->courses;
foreach ($courses as $course) {
echo $course->title;
}
2. Get All Students in a Course
$course = Course::find(1);
$students = $course->students;
foreach ($students as $student) {
echo $student->name;
}
Real-World Example: User ↔ Roles
In an authentication system:
-
A User can have many Roles (Admin, Editor, Customer).
-
A Role can belong to many Users.
Pivot table: role_user
This is used in RBAC (Role-Based Access Control) systems.
🔍 Advantages of Many-to-Many in Laravel
-
Handles complex relationships easily.
-
Pivot tables can store extra data.
-
Works well with eager loading and sync.
-
Matches real-world scenarios like courses, roles, categories, and tags.
4- Has Many Through Relationship
What is Has Many Through?
The Has Many Through relationship allows you to access records of a model through an intermediate model.
👉 Example:
-
A Country has many Users.
-
A User has many Posts.
-
So, a Country can access all of its Posts through its Users.
This avoids writing nested loops and makes queries much cleaner.
Database Structure Example: Country → Users → Posts
| countries table | users table | posts table |
|---|---|---|
| id (PK) | id (PK) | id (PK) |
| name | name | title |
| code | content | |
| ... | country_id (FK → countries.id) | user_id (FK → users.id) |
Step 1: Create Migrations
// countries table
Schema::create('countries', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('code')->unique();
$table->timestamps();
});
// users table
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->foreignId('country_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
// posts table
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content')->nullable();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
Step 2: Define Models
Country Model (Country.php)
class Country extends Model
{
use HasFactory;
public function posts()
{
return $this->hasManyThrough(Post::class, User::class);
}
}
User Model (User.php)
class User extends Model
{
use HasFactory;
public function posts()
{
return $this->hasMany(Post::class);
}
public function country()
{
return $this->belongsTo(Country::class);
}
}
Post Model (Post.php)
class Post extends Model
{
use HasFactory;
public function user()
{
return $this->belongsTo(User::class);
}
}
Step 3: Querying Has Many Through
Get All Posts of a Country
$country = Country::find(1);
$posts = $country->posts;
foreach ($posts as $post) {
echo $post->title;
}
Benefits of Has Many Through
-
Simplifies access to distant relationships.
-
Reduces nested queries.
-
Keeps code clean and maintainable.
-
Works with eager loading.
Real-World Use Case: Hospital System
-
Hospital → has many Doctors.
-
Doctor → has many Appointments.
-
Hospital → directly gets all Appointments via
hasManyThrough.
class Hospital extends Model
{
public function appointments()
{
return $this->hasManyThrough(Appointment::class, Doctor::class);
}
}
Usage:
$hospital = Hospital::find(1);
foreach ($hospital->appointments as $appointment) {
echo $appointment->date;
}
5- Polymorphic Relationships
What is a Polymorphic Relationship?
A Polymorphic Relationship allows a model to belong to more than one type of model using a single association.
Example:
-
A Comment can belong to a Post or a Video.
-
A Tag can belong to a Post or a Product.
-
A Like can belong to a Photo or a Video.
Instead of creating separate tables for each relation, we use one table with a morph type and morph ID.
Real-World Examples of Polymorphic Relationships
-
Comments System
-
A comment can be added on posts, videos, or products.
-
-
Tagging System
-
Tags can be applied to posts, videos, or photos.
-
-
Likes or Reactions
-
A like can belong to a post, comment, or video.
-
Database Structure Example: Comments
We want comments to apply to both posts and videos.
| posts table | videos table | comments table |
|---|---|---|
| id (PK) | id (PK) | id (PK) |
| title | title | body |
| content | url | commentable_id |
| ... | ... | commentable_type |
👉 Here:
-
commentable_id→ The ID of the parent model (post_id or video_id). -
commentable_type→ The parent model type (App\Models\PostorApp\Models\Video).
Step 1: Create Migrations
// posts table
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content')->nullable();
$table->timestamps();
});
// videos table
Schema::create('videos', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('url');
$table->timestamps();
});
// comments table (polymorphic)
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->text('body');
$table->unsignedBigInteger('commentable_id');
$table->string('commentable_type');
$table->timestamps();
});
Step 2: Define Models
Comment Model (Comment.php)
class Comment extends Model
{
use HasFactory;
public function commentable()
{
return $this->morphTo();
}
}
Post Model (Post.php)
class Post extends Model
{
use HasFactory;
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
Video Model (Video.php)
class Video extends Model
{
use HasFactory;
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
Step 3: Querying Polymorphic Relationship
1. Add Comments to a Post
$post = Post::find(1);
$post->comments()->create([
'body' => 'This is a comment on a post.'
]);
2. Add Comments to a Video
$video = Video::find(1);
$video->comments()->create([
'body' => 'This is a comment on a video.'
]);
3. Get Comments of a Post
$post = Post::find(1);
foreach ($post->comments as $comment) {
echo $comment->body;
}
Real-World Example: Tags (Polymorphic Many-to-Many)
Another advanced case is polymorphic many-to-many.
Example: A Tag can belong to posts, videos, or products.
We use a pivot table taggables:
| tags table | taggables table |
|---|---|
| id (PK) | tag_id |
| name | taggable_id |
| ... | taggable_type |
👉 Laravel provides morphToMany and morphedByMany for this.
6- Polymorphic Many-to-Many Relationship
What is Polymorphic Many-to-Many?
A Polymorphic Many-to-Many relationship allows a model to be related to multiple different models using a pivot table.
👉 Example:
-
A Tag can belong to both Posts and Videos.
-
A Category can be assigned to both Products and Services.
-
A Like can be applied to Posts, Photos, or Videos.
Instead of creating multiple pivot tables, Laravel uses one universal pivot table with morphToMany() and morphedByMany().
Real-World Examples of Polymorphic Many-to-Many
-
Tagging System
-
A tag like "Technology" can belong to posts, videos, and podcasts.
-
-
Categories
-
Categories like "Fashion" can belong to products, blogs, or articles.
-
-
Likes / Reactions
-
A like can apply to posts, photos, and videos.
-
Database Structure Example: Tags for Posts & Videos
| tags table | posts table | videos table | taggables table |
|---|---|---|---|
| id (PK) | id (PK) | id (PK) | tag_id (FK → tags.id) |
| name | title | title | taggable_id |
| ... | content | url | taggable_type |
👉 Here:
-
taggable_id→ The ID of the related model (post_id or video_id). -
taggable_type→ The related model type (App\Models\Post,App\Models\Video).
Step 1: Create Migrations
// tags table
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
// posts table
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content')->nullable();
$table->timestamps();
});
// videos table
Schema::create('videos', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('url');
$table->timestamps();
});
// taggables pivot table
Schema::create('taggables', function (Blueprint $table) {
$table->id();
$table->foreignId('tag_id')->constrained()->onDelete('cascade');
$table->unsignedBigInteger('taggable_id');
$table->string('taggable_type');
$table->timestamps();
});
Step 2: Define Models
Tag Model (Tag.php)
class Tag extends Model
{
use HasFactory;
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
}
Post Model (Post.php)
class Post extends Model
{
use HasFactory;
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
Video Model (Video.php)
class Video extends Model
{
use HasFactory;
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
Step 3: Querying Polymorphic Many-to-Many
1. Attach Tags to a Post
$post = Post::find(1);
$post->tags()->attach([1, 2]); // Add tags with IDs 1 and 2
2. Attach Tags to a Video
$video = Video::find(1);
$video->tags()->attach(3); // Add tag with ID 3
3. Get Tags of a Post
$post = Post::find(1);
foreach ($post->tags as $tag) {
echo $tag->name;
}
4. Get Tags of a Video
$video = Video::find(1);
foreach ($video->tags as $tag) {
echo $tag->name;
}
5. Get All Posts of a Tag
$tag = Tag::find(1);
foreach ($tag->posts as $post) {
echo $post->title;
}
6. Get All Videos of a Tag
$tag = Tag::find(1);
foreach ($tag->videos as $video) {
echo $video->title;
}
Real-World Use Case: Blog Tagging System
-
A Tag like "Laravel" applies to both Blog Posts and Tutorial Videos.
-
Instead of creating
post_tagandvideo_tag, we use taggables for all.
👉 This reduces database complexity and keeps relationships flexible.
Final Words
By now, you should have a solid understanding of Laravel Eloquent Relationships—from the simplest One-to-One relationship to advanced concepts like Polymorphic Many-to-Many.
With these skills, you can confidently design and implement real-world applications such as:
-
Blogs with categories, tags, and comments.
-
E-commerce systems with products, orders, and reviews.
-
Social platforms with likes, followers, and media.
-
Learning management systems (LMS) with students, courses, and instructors.