Laravel‘s Eloquent ORM makes it easy to define relationships between models, allowing you to query and manipulate related data efficiently. However, Eloquent relationships can be confusing at first, especially when coming from a background without prior ORM experience.
In this comprehensive guide, we‘ll cover the fundamentals of Eloquent relationships and walk through examples of the main relationship types. By the end, you‘ll have a solid grasp of how to effectively model relational data with Eloquent.
Why Learn Eloquent Relationships?
Before diving in, let‘s review why mastering Eloquent relationships is so valuable:
-
Simplified code – Relate models together to avoid repetitive queries and take advantage of Eloquent conveniences like eager loading. This keeps your code DRY and clean.
-
Powerful querying – Easily fetch related model data with helpful methods like
with()
,has()
, and more. Build complex queries without raw SQL. -
Better performance – Eager load related data efficiently in a single query using
with()
, avoiding the N+1 query problem. -
Better modeling – Structure your models and relations properly to reflect real-world relationships and enforce data integrity.
-
Full ORM capabilities – Fully leverage Eloquent as an ORM by effectively relating and querying model data.
In summary, proficiency with Eloquent relationships allows you to write simpler, more readable code while efficiently querying your database. Let‘s get started!
Relationship Fundamentals
Before defining Eloquent relationships, it‘s important to understand how relational databases represent relationships at a fundamental level.
In relational databases like MySQL, relationships are represented by foreign keys that reference the primary key of related rows. For example:
CREATE TABLE posts (
id INT PRIMARY KEY,
title VARCHAR(255)
);
CREATE TABLE comments (
id INT PRIMARY KEY,
post_id INT,
FOREIGN KEY(post_id) REFERENCES posts(id)
);
Here, the post_id
foreign key in the comments
table references the primary key id
of related rows in the posts
table. This enforces referential integrity and defines the one-to-many relationship between posts and comments.
Eloquent relationships build on this foundation by allowing you to define relationship methods on your models that retrieve related model instances using the foreign key mappings. Let‘s look at some examples.
One To One
A one-to-one relationship is where a single model instance is related to a single instance of another model.
For example, imagine a User
model that has a related Phone
model representing the user‘s phone number. Each User
has one Phone
and each Phone
belongs to one User
.
Here‘s how we can define this in Eloquent:
// User.php
public function phone()
{
return $this->hasOne(Phone::class);
}
// Phone.php
public function user()
{
return $this->belongsTo(User::class);
}
Some key points:
- The
hasOne
andbelongsTo
relationship methods define the one-to-one relationship. - The foreign key is expected to be on the
phones
table referencing theusers
table. - Eloquent will look for the foreign key
user_id
by convention on thephones
table.
Now we can fetch a user‘s phone record using $user->phone
and fetch the owning user of a phone record via $phone->user
.
One To Many
A one-to-many relationship refers to when a single model instance can be related to multiple instances of another model.
For example, think of a blog Post
model that has many related Comment
models. A single post can have unlimited comments.
Here‘s how this relationship can be defined in Eloquent:
// Post.php
public function comments()
{
return $this->hasMany(Comment::class);
}
// Comment.php
public function post()
{
return $this->belongsTo(Post::class);
}
To define a one-to-many relationship, the hasMany
and belongsTo
methods are used again. The foreign key is expected on the comments
table referencing the posts
table.
We can now fetch a post‘s comments via $post->comments
and fetch the parent post of a comment via $comment->post
.
Many To Many
A many-to-many relationship refers to when multiple instances of a model can be related to multiple instances of another model.
For example, imagine a User
model that has a many-to-many relationship with a Role
model. A user can have multiple roles, and a role can be assigned to multiple users.
Many-to-many requires a pivot table to define the relationship, containing foreign keys referencing the primary keys of both models:
CREATE TABLE role_user (
user_id INT,
role_id INT
);
In Eloquent, we would define this using the belongsToMany
method on both models:
// User.php
public function roles()
{
return $this->belongsToMany(Role::class);
}
// Role.php
public function users()
{
return $this->belongsToMany(User::class);
}
Some additional points:
- The pivot table is assumed to be named alphabetically by singular model names like
role_user
. - Custom pivot table names can be specified on the
belongsToMany
definition. - By default, additional pivot table columns are accessible as attributes when fetching related models.
With this relation defined, we can fetch a user‘s roles via $user->roles
and a role‘s users via $role->users
.
Has One Through
The "has one through" relationship allows defining a one-to-one relationship through a pivot table.
For example, imagine users make orders, and each order record is related to exactly one order status:
users orders order_statuses
------ ------- --------------
id id id
name user_id name
order_status_id
We can relate the User
and OrderStatus
models through the pivot orders
table using the hasOneThrough
relation:
// User.php
public function orderStatus()
{
return $this->hasOneThrough(OrderStatus::class, Order::class);
}
Now we can fetch a user‘s order status via $user->orderStatus
, by going "through" the Order
model to reach the related OrderStatus
.
Has Many Through
The "has many through" relationship allows defining a one-to-many relationship through a pivot table.
For example, imagine users can have multiple posts, and each post can have multiple tags. We can relate users to tags through the pivot posts table:
users posts tags
------ ------- ----
id id id
name user_id name
tag_id
The relationship would be defined like:
// User.php
public function tags()
{
return $this->hasManyThrough(Tag::class, Post::class);
}
Now $user->tags
will retrieve all the user‘s tags by joining through the pivot posts
table.
Polymorphic Relations
Polymorphic relations allow a model to be related to multiple other models through a single association.
For example, imagine a tagging system with photos and videos that can be tagged:
posts tags
-------- -------
id id
title name
body
taggable_id
taggable_type
videos
-------
id
name
Here, the tags
table contains polymorphic columns to relate to posts
and videos
in a single relation.
We can define this in Eloquent as:
// Tag.php
public function taggable()
{
return $this->morphTo();
}
Now $tag->taggable
will return either a Post
or Video
instance based on the polymorphic columns. The consuming application code doesn‘t need to know or care what model type is returned.
Eager Loading
When fetching model instances, related data can be "eager loaded" to avoid running additional queries.
For example:
$books = Book::with(‘author‘)->get();
This will fetch all books and their related authors in a single query. Without eager loading, an additional query would run for each book to fetch the author.
Eager loading helps avoid the N+1 query problem and can significantly improve performance.
Demo Project
Let‘s explore some relationship examples in a simple demo project.
Imagine we are building an admin portal for a blog. We‘ll have Users
, Posts
, Comments
, and Tags
models with the following relationships:
User
1:MPost
Post
1:MComment
Post
M:MTag
through pivot tablepost_tag
Comment
1:1Status
First, the migrations:
// users
Schema::create(‘users‘, function (Blueprint $table) {
$table->id();
$table->string(‘name‘);
});
// posts
Schema::create(‘posts‘, function (Blueprint $table) {
$table->id();
$table->foreignId(‘user_id‘)->constrained();
$table->string(‘title‘);
});
// comments
Schema::create(‘comments‘, function (Blueprint $table) {
$table->id();
$table->foreignId(‘post_id‘)->constrained();
$table->text(‘content‘);
});
// statuses
Schema::create(‘statuses‘, function (Blueprint $table) {
$table->id();
$table->string(‘name‘);
});
// tags
Schema::create(‘tags‘, function (Blueprint $table) {
$table->id();
$table->string(‘name‘);
});
// post_tag pivot
Schema::create(‘post_tag‘, function (Blueprint $table) {
$table->foreignId(‘post_id‘)->constrained();
$table->foreignId(‘tag_id‘)->constrained();
});
Next, the model relationships:
// User
public function posts()
{
return $this->hasMany(Post::class);
}
// Post
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function tags()
{
return $this->belongsToMany(Tag::class);
}
// Comment
public function post()
{
return $this->belongsTo(Post::class);
}
public function status()
{
return $this->hasOne(Status::class);
}
// Tag
public function posts()
{
return $this->belongsToMany(Post::class);
}
// Status
public function comment()
{
return $this->belongsTo(Comment::class);
}
Now let‘s test them out with some sample data:
// Create a user
$user = User::create([‘name‘ => ‘John Doe‘]);
// Create a post by the user
$post = $user->posts()->create([‘title‘ => ‘My First Post‘]);
// Create a comment on the post
$comment = $post->comments()->create([‘content‘ => ‘Great post!‘]);
// Create an approved status for the comment
$status = $comment->status()->create([‘name‘ => ‘approved‘]);
// Tag the post
$tag = Tag::firstOrCreate([‘name‘ => ‘Laravel‘]);
$post->tags()->attach($tag);
// Fetch the post user
$post->user->name; // "John Doe"
// Fetch the comment‘s post
$comment->post->title; // "My First Post"
// Fetch the comment‘s status
$comment->status->name; // "approved"
// Fetch the post‘s tags
foreach ($post->tags as $tag) {
echo $tag->name; // "Laravel"
}
// Fetch the user‘s post
foreach ($user->posts as $post) {
echo $post->title; // "My First Post"
}
This confirms that our relationships are correctly defined and working as expected!
Recap
The key points about Eloquent relationships:
- They allow efficiently querying and manipulating related model data
- Foreign keys define relationships at the database level
- Eloquent relations build on top of these foreign keys
- Main relations:
hasOne
,belongsTo
,hasMany
,belongsToMany
,hasOneThrough
,hasManyThrough
- Pivot tables define many-to-many mappings
with()
preloads related data via eager loading- Solid relationship knowledge is key to effectively using Eloquent
I hope this guide has shed light on how Eloquent model relationships work and how to leverage them in your Laravel applications! Correctly structuring your models and relations is critical for efficiently interacting with your database.