Relationships

Defining and using relationships between Models

erDiagram USER ||--o{ POST : hasMany USER ||--|| PROFILE : hasOne POST }o--|| USER : belongsTo POST }o--o{ TAG : belongsToMany USER }o--o{ ROLE : belongsToMany COUNTRY ||--o{ USER : hasMany COUNTRY ||--o{ POST : hasManyThrough COMMENT }o--|| POST : morphTo COMMENT }o--|| VIDEO : morphTo

Eloquent Relationships allow you to define relationships between database tables in an object-oriented and intuitive way. This feature is one of the most powerful aspects of Eloquent ORM that makes working with related data very simple.


Types of Relationships:


1. hasOne: One to one - one model has one instance of another model (example: User hasOne Profile)
2. hasMany: One to many - one model has many instances of another model (example: User hasMany Post)
3. belongsTo: Many to one - one model belongs to one instance of another model (example: Post belongsTo User)
4. belongsToMany: Many to many - one model has many instances of another model and vice versa (example: User belongsToMany Role)
5. hasManyThrough: Through - one model accesses another model through an intermediate model (example: Country hasManyThrough Post through User)
6. morphTo/morphMany: Polymorphic - one model can belong to multiple different models (example: Comment morphTo Post/Video)
7. hasOneThrough: One to one through - similar to hasManyThrough but for one-to-one


Benefits of Using Relationships:


  • <strong>Readability</strong>: More readable and understandable code
  • <strong>Type Safety</strong>: Better type hinting and IDE support
  • <strong>Eager Loading</strong>: Preventing N+1 query problem
  • <strong>Query Building</strong>: Using relationship in query building
  • <strong>Cascading Operations</strong>: Cascade operations on related models
  • <strong>Database Integrity</strong>: Maintaining referential integrity

Conventions:


Laravel uses naming conventions:


  • Foreign key: <code>{model}_id</code> (User → user_id)
  • Pivot table: <code>{model1}_{model2}</code> (user_role)
  • Polymorphic columns: <code>{model}_id</code>, <code>{model}_type</code>

Relationship Methods vs Properties:


  • Relationship method: <code>$user-&gt;posts()</code> - returns relationship query builder
  • Relationship property: <code>$user-&gt;posts</code> - returns collection of related models

Eager Loading:


To prevent N+1 queries, use eager loading:


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

Examples

hasMany Relationship

<?php

class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

// Usage
$user = User::find(1);
$posts = $user->posts; // Collection
$postCount = $user->posts()->count(); // Query builder

// Create related model
$user->posts()->create([
    'title' => 'New Post',
    'content' => 'Post content'
]);

A user can have many posts. Using relationship method and property.

belongsTo Relationship

<?php

class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
    
    // With custom foreign key
    public function author()
    {
        return $this->belongsTo(User::class, 'author_id');
    }
}

// Usage
$post = Post::find(1);
$user = $post->user; // User model

// Associate
$post->user()->associate($user);
$post->save();

A post belongs to a user. Foreign key can be customized.

belongsToMany Relationship

<?php

class User extends Model
{
    public function roles()
    {
        return $this->belongsToMany(Role::class, 'user_role')
            ->withPivot('created_at', 'expires_at')
            ->withTimestamps();
    }
}

class Role extends Model
{
    public function users()
    {
        return $this->belongsToMany(User::class);
    }
}

// Usage
$user = User::find(1);
$roles = $user->roles;

// Attach
$user->roles()->attach($roleId, ['expires_at' => now()->addYear()]);

// Sync
$user->roles()->sync([1, 2, 3]);

// Detach
$user->roles()->detach($roleId);

Many to many relationship with pivot table. Pivot columns can be accessed.

hasManyThrough Relationship

<?php

class Country extends Model
{
    public function posts()
    {
        return $this->hasManyThrough(
            Post::class,
            User::class,
            'country_id', // Foreign key on users table
            'user_id',    // Foreign key on posts table
            'id',         // Local key on countries table
            'id'          // Local key on users table
        );
    }
}

// Usage
$country = Country::find(1);
$posts = $country->posts; // All posts from users in this country

Accessing posts through users. Country → User → Post.

Polymorphic Relationship

<?php

class Comment extends Model
{
    public function commentable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

class Video extends Model
{
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

// Usage
$comment = Comment::find(1);
$commentable = $comment->commentable; // Post or Video

$post = Post::find(1);
$comments = $post->comments;

Polymorphic relationship where one model can belong to multiple different models.

Relationship with Constraints

<?php

class User extends Model
{
    public function publishedPosts()
    {
        return $this->hasMany(Post::class)->where('status', 'published');
    }
    
    public function recentPosts()
    {
        return $this->hasMany(Post::class)
            ->where('created_at', '>', now()->subDays(7))
            ->orderBy('created_at', 'desc');
    }
}

// Usage
$user = User::find(1);
$publishedPosts = $user->publishedPosts;
$recentPosts = $user->recentPosts;

Defining relationship with constraints to filter results.

Eager Loading with Relationships

<?php

// Eager load single relationship
$users = User::with('posts')->get();

// Eager load multiple relationships
$users = User::with(['posts', 'profile', 'roles'])->get();

// Nested eager loading
$users = User::with('posts.comments')->get();

// Eager load with constraints
$users = User::with(['posts' => function ($query) {
    $query->where('published', true);
}])->get();

// Lazy eager loading
$users = User::all();
$users->load('posts');

Using eager loading to prevent N+1 queries.

Use Cases

  • Defining relationships between entities in database
  • Easy access to related data
  • Query building with relationships
  • Cascading operations on related models
  • Polymorphic associations for flexible data structures
  • Many-to-many relationships with pivot tables
  • Accessing data through intermediate models

Common Mistakes

  • N+1 query problem with lazy loading relationships
  • Forgetting eager loading for relationships
  • Using relationship property instead of method for query building
  • Defining relationship with wrong foreign key
  • Forgetting return statement in relationship method
  • Using relationship in loop without eager loading

Best Practices

  • Always use eager loading for relationships
  • Use relationship method for query building
  • Use relationship property for accessing data
  • Define foreign keys correctly
  • Use constraints in relationships
  • Define pivot columns with withPivot()
  • Keep relationship naming consistent

Edge Cases

  • Relationships with custom foreign keys
  • Polymorphic relationships with multiple types
  • Many-to-many relationships with pivot data
  • Relationships with soft deletes
  • Circular relationships
  • Relationships with multiple database connections
  • Relationships with composite keys

Performance Notes

  • Eager loading can solve N+1 problem
  • Use select() for selecting specific columns in relationships
  • Use withCount() for counting relationships
  • Use exists() for checking existence
  • Use whereHas() for filtering with relationships
  • Indexes for foreign keys are important

Security Notes

  • Ensure relationships are properly authorized
  • Use user input in relationship queries with caution
  • Ensure pivot data is validated
  • Use mass assignment in relationships with caution
  • Ensure polymorphic relationships are secure

Interview Points

  • What are the types of relationships in Eloquent?
  • What is the difference between hasMany and belongsTo?
  • What is N+1 query problem and how is it solved?
  • What is polymorphic relationship and when is it used?
  • What is the difference between relationship method and property?
  • How can you define relationship with constraints?

Version Notes

  • Laravel 11.x: Improved performance in relationship queries
  • Laravel 11.x: Better support for eager loading
  • Laravel 10.x: Improved polymorphic relationships
  • Laravel 9.x: Improved relationship constraints