In real applications, data rarely stands alone: one user has many articles, one article has many tags. Handling these inter-table relationships manually with JOIN queries can be tiring. Eloquent Relationships in Laravel make it far more concise and readable. This article covers the two most commonly used relationships — One to Many and Many to Many — along with how to avoid a common performance pitfall.
What Is an Eloquent Relationship?
Eloquent is Laravel's ORM (Object Relational Mapping). Instead of writing SQL JOINs, you define relationships between models as methods, then access them like ordinary properties. The code becomes more declarative and easier to maintain.
One to Many
A classic example: one User writes many Posts. In the User model, add a posts() method:
// app/Models/User.php
public function posts()
{
return $this->hasMany(Post::class);
}
Conversely, one Post belongs to one User. In the Post model:
// app/Models/Post.php
public function user()
{
return $this->belongsTo(User::class);
}
The requirement is that the posts table has a user_id column (foreign key). Now you can access the relationships:
$user = User::find(1);
foreach ($user->posts as $post) { // all posts belonging to the user
echo $post->title;
}
$post = Post::find(5);
echo $post->user->name; // the post author's name
Many to Many
Example: one Post has many Tags, and one Tag is used by many Posts. This relationship needs a pivot table (for example post_tag) containing post_id and tag_id.
// app/Models/Post.php
public function tags()
{
return $this->belongsToMany(Tag::class);
}
// app/Models/Tag.php
public function posts()
{
return $this->belongsToMany(Post::class);
}
Adding and removing relationships becomes very easy with attach, detach, and sync:
$post->tags()->attach($tagId); // add one relationship
$post->tags()->detach($tagId); // remove one relationship
$post->tags()->sync([1, 2, 3]); // set exactly to tags 1, 2, 3
The N+1 Pitfall and Eager Loading
This is the performance problem that most often affects beginners. Look at this code:
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name; // 1 EXTRA query per post!
}
If there are 100 posts, Laravel runs 1 query for the posts + 100 queries for the users = 101 queries. The solution is eager loading with with():
$posts = Post::with('user')->get(); // just 2 queries total
Always use with() when you know a relationship will be accessed inside a loop. This is one of the most impactful optimizations in a Laravel application.
Frequently Asked Questions (FAQ)
When do I use hasMany vs belongsTo?
hasMany goes on the "parent" model (the one that owns many), and belongsTo on the "child" model (the one that is owned). The foreign key always lives in the table of the belongsTo model.
What is a pivot table?
An intermediate table in a Many to Many relationship that stores the pairs of foreign keys from both tables. Laravel guesses its name from the combination of the two model names (in alphabetical order), for example post_tag.
How do I get data from the pivot table?
Add ->withPivot('column') to the relationship definition, then access it via $post->tags[0]->pivot->column.
Conclusion
Eloquent Relationships make managing data across tables far more concise: One to Many with hasMany/belongsTo, and Many to Many with belongsToMany + a pivot table. Don't forget to use with() to avoid the N+1 problem that silently slows your application down.
Want to understand the foundation first? Read How to Create Table Relationships in MySQL with Foreign Keys and How to Create Migrations and Seeders in Laravel to set up the table structure.