Laravel Eloquent Relationships(ORM)-Full Topic Coverage
✅ What are Eloquent Relationships?
ORM (Object-Relational Mapping) is a programming technique that allows you to interact with a relational database using object-oriented code instead of raw SQL queries.
Laravel Eloquent is Laravel’s built-in ORM that provides an ActiveRecord implementation, allowing each database table to have a corresponding Model that interacts with that table..
🔹 In simple terms:
Eloquent lets you interact with your database using PHP classes and objects instead of writing raw SQL queries.
Table of Contents
Eloquent relationships are a way of defining relationships between models in Laravel. They provide an easy, readable, and efficient way to interact with related data using Laravel’s ORM (Object-Relational Mapping).
🔗 Types of Relationships in Laravel
- One to One
- One to Many
- Many to Many
- Has One Through
- Has Many Through
- Polymorphic One to One
- Polymorphic One to Many
- Polymorphic Many to Many (morphToMany & morphedByMany)
1. 🧍 One to One
🔹 Example:
A user has one profile.
A one-to-one relationship is used when one record in a table is associated with one record in another table.
Migration:
Schema::create('profiles', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('bio');
});
Model:
// User.php
public function profile()
{
return $this->hasOne(Profile::class); // if you already set foreign_key
}
/*public function profile()
{
return $this->hasOne(Profile::class,'profile_id_fk','id');
// $this->hasOne(Profile::class,'foreign_key',local_key');
}*/
// Profile.php
public function user()
{
return $this->belongsTo(User::class);
}Usage:
$user = User::find(1);
echo $user->profile->bio;
//OR
$user = User::with('profile')->get(); // profile is function name2. 👨👧 One to Many
🔹 Example:
A post has many comments.
A one-to-many relationship is used when one record in a table has many related records in another table.
Model:
// Post.php
public function comments()
{
return $this->hasMany(Comment::class);
}
// Comment.php
public function post()
{
return $this->belongsTo(Post::class);
}Usage:
$post = Post::find(1);
foreach ($post->comments as $comment) {
echo $comment->body;
}3. 👬 Many to Many
🔹 Example:
A user can have many roles and a role can belong to many users.
A many-to-many relationship is used when each record in one table may be related to many records in another table and vice versa.
This needs a pivot table (like role_user).
Pivot Table Migration:
Schema::create('role_user', function (Blueprint $table) {
$table->foreignId('user_id')->constrained();
$table->foreignId('role_id')->constrained();
});Model:
// User.php
public function roles()
{
return $this->belongsToMany(Role::class);
}
// Role.php
public function users()
{
return $this->belongsToMany(User::class);
}Usage:
$user->roles()->attach($roleId);
$user->roles()->detach($roleId);
$user->roles()->sync([1, 2, 3]);4. 🔀 Has One Through
🔹 Example:
A country has one government, through a state.
A “has-one-through” relationship provides a shortcut for accessing a model through an intermediate relation.
Model:
// Country.php
public function government()
{
return $this->hasOneThrough(Government::class, State::class);
}5. 🔁 Has Many Through
🔹 Example:
A country has many posts through states.
In Laravel, the hasManyThrough relationship is used to define a “has-many-through” connection between two models that do not have a direct relationship, but are linked through a third model.
Model:
// Country.php
public function posts()
{
return $this->hasManyThrough(Post::class, State::class);
}
// country belongs to state
// state belongs to posts
// not direct relationship between post and country
//Country -> hasMany(State)
//State -> hasMany(Post)
/* So:
🔹 A Country has many States
🔹 Each State has many Posts
🔹 Therefore, a Country has many Posts through States */
🔍 Optional: If your foreign/primary keys are not standard, specify them manually:
return $this->hasManyThrough(
Post::class, // Final model
State::class, // Intermediate model
'country_id', // Foreign key on states table
'state_id', // Foreign key on posts table
'id', // Local key on countries table
'id' // Local key on states table
);6. 🌀 Polymorphic One to One
🔹 Example:
An image can belong to either a user or a post.
Used when different models can share the same related model.
Migration:
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->morphs('imageable');
$table->string('url');
});Model:
// Image.php
public function imageable()
{
return $this->morphTo();
}
// Post.php or User.php
public function image()
{
return $this->morphOne(Image::class, 'imageable');
}7. 📸 Polymorphic One to Many
🔹 Example:
A comment can belong to both posts and videos.
Used when different models can share the same related model.
Model:
// Comment.php
public function commentable()
{
return $this->morphTo();
}
// Post.php or Video.php
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}8. 🌍 Polymorphic Many to Many
🔹 Example:
A tag can belong to posts or videos.
A model can belong to multiple models on a single association.
Model:
// Tag.php
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
// Post.php or Video.php
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}📦 Eager Loading Relationships
$posts = Post::with('comments')->get();✅ Benefit:
Avoids N+1 Query Problem, improves performance.
🔧 Relationship Methods
| Method | Description |
|---|---|
hasOne() | One-to-One |
hasMany() | One-to-Many |
belongsTo() | Child side of one-to-one/many |
belongsToMany() | Many-to-Many |
morphTo() | Polymorphic Parent |
morphOne() | One-to-One Polymorphic |
morphMany() | One-to-Many Polymorphic |
morphToMany() | Many-to-Many Polymorphic |
morphedByMany() | Inverse of morphToMany |
🟢 Advantages of Eloquent Relationships
| Advantage | Explanation |
|---|---|
| ✅ Readable & expressive syntax | Easy to define and use |
| ✅ Fewer queries with eager loading | Avoid N+1 problems |
| ✅ Chainable queries | More flexible querying |
| ✅ Automatic foreign key conventions | Reduces boilerplate |
| ✅ Relationship-based CRUD | Attach/detach/sync/associate etc. |
| ✅ Maintains Data Integrity | With cascade deletes and constraints |
💡 Tips & Best Practices
- Always eager load when accessing nested relationships.
- Use accessors/mutators with relationships for clean logic.
- Use
withCount()for quick counts of related models. - Prefer
hasManyover manually writing joins. - Use
syncWithoutDetaching()to avoid losing previous pivot data.
Is Eloquent better than raw SQL?
It depends:
Use Eloquent for rapid development, simplicity, and readability.
Use raw SQL or Query Builder for complex joins or performance-sensitive queries.
How does Eloquent know which table to use?
By default, Eloquent converts the model name to a plural, snake_case table name.
E.g., User model → users table.
You can override it using:
protected $table = ‘custom_table_name’;
What is the use of $fillable and $guarded in Eloquent?
They protect against mass assignment vulnerabilities.
// Allow these fields for mass assignment
protected $fillable = [‘name’, ’email’];
// Block these fields (if using $guarded)
protected $guarded = [‘id’];
Does Eloquent support transactions?
Yes. Laravel supports database transactions using DB::beginTransaction(), commit(), and rollback().
DB::beginTransaction();
try {
// Do operations
DB::commit();
} catch (\Exception $e) {
DB::rollback();
}
Can I use Eloquent without Laravel?
Yes, but it requires some setup. Eloquent can be used as a standalone package in non-Laravel applications.
What is eager loading in Eloquent?
It loads relationships in a single query to prevent the N+1 problem.
// Without eager loading
$posts = Post::all(); // Will run a separate query for each post’s user
// With eager loading
$posts = Post::with(‘user’)->get(); // Optimized
What is lazy loading in Eloquent?
It loads relationships when accessed, not during the initial query. This may cause performance issues in loops.
