Laravel Eloquent Relationships

Laravel Eloquent relationships describe how your tables connect, such as a user having many posts or a post having many tags. You define them as methods on models and then navigate related data like ordinary properties.

One to Many: hasMany and belongsTo

Laravel assumes posts has a user_id column. Accessing $user->posts as a property runs the query and returns a collection.

<?php
class User extends Authenticatable
{
    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}

class Post extends Model
{
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

$user = User::find(1);
echo $user->posts->count(), "\n";
echo Post::find(3)->user->name;
3
Naeem

One to One: hasOne

<?php
public function profile(): HasOne
{
    return $this->hasOne(Profile::class);
}

echo $user->profile?->city ?? 'No profile';
Karachi

Many to Many: belongsToMany

A post can have many tags and a tag many posts. This needs a pivot table named post_tag with post_id and tag_id.

<?php
class Post extends Model
{
    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(Tag::class)->withTimestamps();
    }
}

$post->tags()->attach([1, 2]);
$post->tags()->sync([2, 3]);   // keep only 2 and 3
echo $post->tags->pluck('name')->join(', ');
php, laravel

Creating Related Records

Creating through the relationship fills the foreign key automatically.

<?php
$post = $user->posts()->create([
    'title' => 'My second post',
    'slug' => 'my-second-post',
    'body' => '...',
]);
echo $post->user_id;
1

Eager Loading and the N+1 Problem

Looping over posts and reading $post->user runs one extra query per post (N+1 queries). Eager load with with() to fetch everything in two queries. withCount adds counts without loading rows.

<?php
// Bad: 1 + N queries
foreach (Post::all() as $post) { echo $post->user->name; }

// Good: 2 queries
$posts = Post::with(['user', 'tags'])->withCount('comments')->latest()->get();
foreach ($posts as $post) {
    echo "{$post->title} by {$post->user->name} ({$post->comments_count} comments)\n";
}
Blade tips by Naeem (4 comments)
Eloquent basics by Sara (1 comments)

Querying by Relationship

<?php
$active = User::has('posts', '>=', 3)->get();
$laravelPosts = Post::whereHas('tags', fn ($q) => $q->where('name', 'laravel'))->get();
echo $active->count(), " ", $laravelPosts->count();
2 7

Frequently asked questions

What is the N+1 query problem in Laravel?

It happens when you load a list and then lazily load a relationship for each item, causing one query per row. Fix it with eager loading: Model::with("relation").

What is a pivot table in Laravel?

The join table for a many-to-many relationship, named with both singular model names in alphabetical order, such as post_tag.

What is the difference between attach and sync?

attach adds new pivot rows; sync makes the pivot match exactly the IDs you pass, removing any others.