Eloquent Relationships Mastery
Master all relationship types: hasOne, hasMany, belongsTo, belongsToMany, and polymorphic relationships
Eloquent relationships are the heart of your application's data model. They define how your models relate to each other and provide a fluent, expressive way to work with related data. Understanding when to use each type is crucial for building maintainable applications.
Relationship Types:
• One-to-One (hasOne): User has one Profile
• One-to-Many (hasMany): User has many Posts
• Inverse (belongsTo): Post belongs to User
• Many-to-Many (belongsToMany): User belongs to many Roles
• Has Many Through: Country has many Posts through Users
• Polymorphic: Comment can belong to Post OR Video
• Many-to-Many Polymorphic: Tag can belong to Post, Video, etc.
Real-World Scenario:
Building a social media platform requires understanding all these relationships: Users have Posts, Posts have Comments, Users have many Roles (admin, moderator), and Tags can be attached to Posts, Videos, and Photos (polymorphic).
The Power:
Once defined, relationships let you access related data naturally: $user->posts, $post->comments()->where('approved', true)->get()
Relationship Types:
• One-to-One (hasOne): User has one Profile
• One-to-Many (hasMany): User has many Posts
• Inverse (belongsTo): Post belongs to User
• Many-to-Many (belongsToMany): User belongs to many Roles
• Has Many Through: Country has many Posts through Users
• Polymorphic: Comment can belong to Post OR Video
• Many-to-Many Polymorphic: Tag can belong to Post, Video, etc.
Real-World Scenario:
Building a social media platform requires understanding all these relationships: Users have Posts, Posts have Comments, Users have many Roles (admin, moderator), and Tags can be attached to Posts, Videos, and Photos (polymorphic).
The Power:
Once defined, relationships let you access related data naturally: $user->posts, $post->comments()->where('approved', true)->get()
Code Examples
One-to-One Relationship
💡 Solution
php
<?php
// User has one Profile
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
// Convention: profile has user_id column
}
}
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
// Usage:
$user = User::find(1);
$bio = $user->profile->bio; // Access profile
$username = $user->profile->user->name; // Back to user
// Creating related records:
$user = User::find(1);
$user->profile()->create([
'bio' => 'Laravel developer',
'avatar' => 'avatar.jpg',
]);
// Or:
$profile = new Profile(['bio' => '...']);
$user->profile()->save($profile);
// Real-world example: User settings
class User extends Model
{
public function settings()
{
return $this->hasOne(UserSettings::class);
}
}
$user->settings->notifications_enabled;
$user->settings->theme;
💡 One-to-One relationships link two models where each record in one table corresponds to exactly one record in another table.
One-to-Many Relationship
💡 Solution
php
<?php
// User has many Posts
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
// Convention: posts have user_id column
}
}
class Post extends Model
{
public function author()
{
return $this->belongsTo(User::class, 'user_id');
}
}
// Usage:
$user = User::find(1);
$posts = $user->posts; // Collection of posts
$count = $user->posts()->count(); // Query builder
$published = $user->posts()->where('published', true)->get();
// Creating related records:
$user = User::find(1);
$user->posts()->create([
'title' => 'My New Post',
'content' => '...',
]);
// Or create many:
$user->posts()->createMany([
['title' => 'Post 1', 'content' => '...'],
['title' => 'Post 2', 'content' => '...'],
]);
// Inverse:
$post = Post::find(1);
$authorName = $post->author->name;
// Querying relationships:
$users = User::has('posts', '>=', 5)->get(); // Users with 5+ posts
$users = User::whereHas('posts', function ($query) {
$query->where('published', true);
})->get(); // Users with published posts
💡 One-to-Many is the most common relationship. A parent has multiple children, each child belongs to one parent.
Many-to-Many Relationship
💡 Solution
php
<?php
// User belongs to many Roles (and vice versa)
// Requires pivot table: role_user (alphabetically ordered)
class User extends Model
{
public function roles()
{
return $this->belongsToMany(Role::class)
->withPivot(['assigned_at', 'assigned_by'])
->withTimestamps();
}
}
class Role extends Model
{
public function users()
{
return $this->belongsToMany(User::class);
}
}
// Pivot table migration:
Schema::create('role_user', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('role_id')->constrained()->onDelete('cascade');
$table->timestamp('assigned_at')->nullable();
$table->unsignedBigInteger('assigned_by')->nullable();
$table->timestamps();
});
// Usage:
$user = User::find(1);
$roles = $user->roles; // All roles for this user
$hasRole = $user->roles->contains('name', 'admin');
// Attaching roles:
$user->roles()->attach($roleId); // Add role
$user->roles()->attach($roleId, ['assigned_at' => now()]); // With pivot data
// Detaching:
$user->roles()->detach($roleId); // Remove specific role
$user->roles()->detach(); // Remove all roles
// Syncing (set exact roles):
$user->roles()->sync([1, 2, 3]); // User will have only these roles
// Toggle:
$user->roles()->toggle([1, 2]); // Attach if not attached, detach if attached
// Accessing pivot data:
foreach ($user->roles as $role) {
echo $role->name;
echo $role->pivot->assigned_at; // Pivot table data
echo $role->pivot->assigned_by;
}
// Querying with pivot conditions:
$user->roles()->wherePivot('assigned_at', '>', now()->subDays(30))->get();
💡 Many-to-Many relationships use a pivot table to link two models where each can have multiple of the other.
Polymorphic Relationships
💡 Solution
php
<?php
// Comments can belong to Posts OR Videos
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
}
class Post extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
class Video extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
// Comments table migration:
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->text('body');
$table->morphs('commentable'); // Adds commentable_id and commentable_type
// commentable_type stores: App\Models\Post or App\Models\Video
// commentable_id stores: the post or video ID
$table->timestamps();
});
// Usage:
$post = Post::find(1);
$post->comments()->create(['body' => 'Great post!']);
$video = Video::find(1);
$video->comments()->create(['body' => 'Nice video!']);
// Retrieving:
$comment = Comment::find(1);
$parent = $comment->commentable; // Returns Post or Video instance
if ($parent instanceof Post) {
echo "Comment on post: " . $parent->title;
} elseif ($parent instanceof Video) {
echo "Comment on video: " . $parent->title;
}
// Real-world: Tagging system
class Tag extends Model
{
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
}
class Post extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
// Usage:
$tag = Tag::find(1);
$tag->posts; // All posts with this tag
$tag->videos; // All videos with this tag
💡 Polymorphic relationships allow a model to belong to multiple other models on a single association. Perfect for features like comments, tags, or likes.