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:

In this post, we’ll walk through Laravel one-to-many relationships step by step, covering:

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:

In database terms:

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:

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:

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:

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:

This is known as the N+1 query problem.


Fix it with eager loading

$users = User::with('posts')->get();

Now Laravel executes:

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:


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


Why Laravel’s Relationship System Scales

Laravel’s one-to-many relationships scale from:

Because:


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:

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:

Just let me know what you want next.

Leave a Reply

Your email address will not be published. Required fields are marked *