Laravel’s Eloquent ORM is one of the framework’s strongest features. It allows you to work with your database using expressive, readable PHP instead of writing raw SQL for every query. At the heart of Eloquent is its relationship system, and one of the most common — and most important — relationships you’ll use is the one-to-many relationship.
If you’re building any real application, you’ll encounter patterns like:
- A user has many posts
- A post has many comments
- A category has many products
- A project has many tasks
In this post, we’ll walk through Laravel one-to-many relationships step by step, covering:
- What a one-to-many relationship is
- How to design your database tables
- How to define relationships in Eloquent models
- How to create, retrieve, update, and delete related records
- Common pitfalls and best practices
- Real-world usage patterns
By the end, you’ll understand not just how to use one-to-many relationships, but why they work the way they do.
What Is a One-to-Many Relationship?
A one-to-many relationship exists when:
One record in a table can be associated with many records in another table.
For example:
- One user → many posts
- One post → many comments
In database terms:
- The “many” table contains a foreign key
- The foreign key references the “one” table’s primary key
Laravel’s Eloquent ORM makes working with this relationship clean, expressive, and intuitive.
Example Scenario: Users and Posts
We’ll use a classic example throughout this post:
- A User can have many Post records
- Each Post belongs to exactly one User
This example maps cleanly to real applications like blogs, SaaS dashboards, and content platforms.
Step 1: Database Table Structure
Before writing any PHP code, it’s critical to understand the database structure.
Users table
users
- id (primary key)
- name
- email
- password
- created_at
- updated_at
Posts table
posts
- id (primary key)
- user_id (foreign key)
- title
- body
- created_at
- updated_at
The key point is the user_id column in the posts table. This column links each post back to its user.
Step 2: Creating Migrations
Laravel makes this easy with Artisan.
php artisan make:migration create_posts_table
Inside the migration:
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')
->constrained()
->onDelete('cascade');
$table->string('title');
$table->text('body');
$table->timestamps();
});
Why onDelete('cascade') matters
This ensures:
- When a user is deleted
- All their posts are automatically deleted
This prevents orphaned records and keeps your database clean.
Step 3: Defining the Relationship in Eloquent Models
Now comes the Eloquent magic.
User Model (One → Many)
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
This tells Laravel:
A user has many posts.
By convention, Laravel assumes:
- The foreign key is
user_id - The local key is
id
If you follow conventions, you don’t need to configure anything else.
Post Model (Many → One)
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
This tells Laravel:
A post belongs to a user.
Step 4: Retrieving Related Data
Once relationships are defined, accessing related data becomes incredibly simple.
Get all posts for a user
$user = User::find(1);
$posts = $user->posts;
This returns a collection of Post models.
Get the user for a post
$post = Post::find(1);
$user = $post->user;
Laravel handles the SQL under the hood.
Looping through related records
foreach ($user->posts as $post) {
echo $post->title;
}
Readable, expressive, and clean.
Step 5: Eager Loading vs Lazy Loading
One of the most important performance concepts in Laravel relationships is eager loading.
The N+1 problem
This code looks innocent:
$users = User::all();
foreach ($users as $user) {
foreach ($user->posts as $post) {
echo $post->title;
}
}
But it results in:
- 1 query to load users
- N queries to load posts for each user
This is known as the N+1 query problem.
Fix it with eager loading
$users = User::with('posts')->get();
Now Laravel executes:
- 1 query for users
- 1 query for posts
Always eager load relationships when you know you’ll need them.
Step 6: Creating Related Records
Laravel provides multiple clean ways to create related models.
Create a post for a user
$user = User::find(1);
$user->posts()->create([
'title' => 'My First Post',
'body' => 'This is the content of the post.'
]);
Laravel automatically sets user_id for you.
Mass assignment protection
Make sure your Post model allows mass assignment:
class Post extends Model
{
protected $fillable = ['title', 'body'];
}
Step 7: Updating Related Records
Updating works exactly as you’d expect.
$post = $user->posts()->where('id', 1)->first();
$post->update([
'title' => 'Updated Title'
]);
Using the relationship ensures:
- The post belongs to the user
- You don’t accidentally modify unrelated records
Step 8: Deleting Related Records
Delete a single related record
$user->posts()->where('id', 1)->delete();
Delete all related records
$user->posts()->delete();
Be careful with this — it’s powerful.
Step 9: Counting Related Records
Laravel makes counts extremely easy.
Count posts for a user
$user->posts()->count();
Load counts efficiently
$users = User::withCount('posts')->get();
foreach ($users as $user) {
echo $user->posts_count;
}
This avoids loading entire collections just to count records.
Step 10: Filtering and Querying Relationships
One-to-many relationships integrate seamlessly with query builders.
Get only published posts
$user->posts()->where('published', true)->get();
Order related records
$user->posts()->orderBy('created_at', 'desc')->get();
Limit related records
$user->posts()->latest()->take(5)->get();
Perfect for dashboards and profile pages.
Common Mistakes to Avoid
❌ Forgetting eager loading
Leads to serious performance issues.
❌ Not enforcing foreign key constraints
Always use database constraints, not just application logic.
❌ Overloading controllers
Move relationship logic into models or services where appropriate.
❌ Naming relationships incorrectly
Use plural names (posts, not post) for hasMany relationships.
Real-World Best Practices
- Always eager load relationships in APIs
- Store relationship logic in models
- Use DTOs or API resources for responses
- Avoid deeply nested relationship chains
- Add indexes to foreign keys
Why Laravel’s Relationship System Scales
Laravel’s one-to-many relationships scale from:
- simple CRUD apps
- to large SaaS platforms
- to multi-tenant systems
Because:
- The API is consistent
- The SQL is optimized
- The conventions reduce errors
- The abstraction doesn’t hide important behavior
Final Thoughts
Laravel one-to-many relationships are simple to use, but incredibly powerful when applied correctly. They allow you to model real-world data naturally, write expressive code, and maintain performance even as your application grows.
If you understand:
- database structure
- Eloquent conventions
- eager loading
- relationship querying
You’ll be able to build clean, maintainable Laravel applications without fighting your data layer.
If you’d like follow-up posts, I can cover:
- One-to-one relationships
- Many-to-many relationships
- Polymorphic relationships
- Advanced eager loading strategies
- Optimizing Eloquent for large datasets
Just let me know what you want next.