Relationships in Laravel Eloquent
Laravel's Eloquent ORM provides several types of database relationships that make working with related data intuitive and efficient. Here's a detailed guide to all relationship types:
1. One-to-One
A basic relationship where one model owns exactly one of another model.
php
// User has one Phone class User extends Model { public function phone() { return $this->hasOne(Phone::class); // Optional: specify foreign key and local key // return $this->hasOne(Phone::class, 'foreign_key', 'local_key'); } } // Inverse relationship in Phone model class Phone extends Model { public function user() { return $this->belongsTo(User::class); } } // Usage: $phone = User::find(1)->phone; $user = Phone::find(1)->user;
2. One-to-Many
A relationship where one model has many of another model.
php
// Post has many Comments class Post extends Model { public function comments() { return $this->hasMany(Comment::class); } } // Inverse relationship in Comment model class Comment extends Model { public function post() { return $this->belongsTo(Post::class); } } // Usage: $comments = Post::find(1)->comments; $post = Comment::find(1)->post; // Create related model $post->comments()->create(['body' => 'A new comment']);
3. Many-to-Many
A more complex relationship that requires an intermediate join table.
php
// User belongs to many Roles (and vice versa) class User extends Model { public function roles() { return $this->belongsToMany(Role::class); // With custom pivot table name: // return $this->belongsToMany(Role::class, 'role_user'); // With custom keys: // return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id'); } } // Inverse in Role model class Role extends Model { public function users() { return $this->belongsToMany(User::class); } } // Usage: $user = User::find(1); foreach ($user->roles as $role) { // } // Attach/detach/sync relationships $user->roles()->attach($roleId); $user->roles()->detach($roleId); $user->roles()->sync([1, 2, 3]); // Only these IDs will be attached
Pivot Tables with Extra Columns
php
// Defining the relationship with pivot columns public function roles() { return $this->belongsToMany(Role::class)->withPivot('active', 'created_by'); } // Accessing pivot data foreach ($user->roles as $role) { echo $role->pivot->active; } // Filtering by pivot columns $users = User::whereHas('roles', function($query) { $query->where('active', true); })->get();
4. Has-One-Through & Has-Many-Through
These define "distant" relationships through intermediate models.
php
// Supplier -> has one -> User -> has one -> History // (Get supplier's history through user) class Supplier extends Model { public function history() { return $this->hasOneThrough( History::class, User::class, 'supplier_id', // Foreign key on users table 'user_id', // Foreign key on history table 'id', // Local key on suppliers table 'id' // Local key on users table ); } } // Country -> has many -> User -> has many -> Post // (Get country's posts through users) class Country extends Model { public function posts() { return $this->hasManyThrough( Post::class, User::class, 'country_id', // Foreign key on users table 'user_id', // Foreign key on posts table 'id', // Local key on countries table 'id' // Local key on users table ); } }
5. Polymorphic Relationships
Allows a model to belong to more than one other model on a single association.
One-to-One Polymorphic
php
// Image can belong to either User or Post class Image extends Model { public function imageable() { return $this->morphTo(); } } class User extends Model { public function image() { return $this->morphOne(Image::class, 'imageable'); } } class Post extends Model { public function image() { return $this->morphOne(Image::class, 'imageable'); } } // Usage: $image = User::find(1)->image; $imageable = $image->imageable; // Returns User or Post instance
One-to-Many Polymorphic
php
// Comment can belong to either Video or Post 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'); } }
Many-to-Many Polymorphic
More complex relationship with a pivot table.
php
// Post and Video can have many Tags, and Tag can belong to many Posts/Videos class Post extends Model { public function tags() { return $this->morphToMany(Tag::class, 'taggable'); } } class Video extends Model { public function tags() { return $this->morphToMany(Tag::class, 'taggable'); } } class Tag extends Model { public function posts() { return $this->morphedByMany(Post::class, 'taggable'); } public function videos() { return $this->morphedByMany(Video::class, 'taggable'); } }
Eager Loading Relationships
To prevent the N+1 query problem:
php
// Load multiple relationships $posts = Post::with(['comments', 'tags'])->get(); // Nested eager loading $posts = Post::with('comments.author')->get(); // Conditional eager loading $posts = Post::with(['comments' => function($query) { $query->where('approved', true); }])->get(); // Lazy eager loading (after initial query) $posts = Post::all(); $posts->load('comments');
Querying Relationship Existence
php
// Get all posts with at least one comment $posts = Post::has('comments')->get(); // Get all posts with 3+ comments $posts = Post::has('comments', '>=', 3)->get(); // More complex conditions $posts = Post::whereHas('comments', function($query) { $query->where('content', 'like', '%code%'); })->get(); // Doesn't have relationship $posts = Post::doesntHave('comments')->get();
Saving Related Models
// Save a single related model
$comment = new Comment(['body' => 'A new comment.']);
$post = Post::find(1);
$post->comments()->save($comment);
// Save many related models
$post->comments()->saveMany([
new Comment(['body' => 'First comment']),
new Comment(['body' => 'Second comment']),
]);
// Create (save and return model)
$comment = $post->comments()->create([
'body' => 'A new comment.',
]);
// Update related models
$post->comments()->where('id', 1)->update(['body' => 'Updated']);