Eloquent Relationships Explained With Real Examples
hasMany, belongsToMany, hasManyThrough, morphTo — the docs explain the syntax. This explains when you actually reach for each one.
Eloquent relationships are the part of Laravel most people half-learn. You memorise hasMany, use it everywhere, and reach for raw joins whenever something gets awkward. Here is when each one actually earns its place.
hasOne / belongsTo — One-to-One
A user has one profile. The foreign key lives on the child: profiles.user_id.
// User
public function profile() { return $this->hasOne(Profile::class); }
// Profile
public function user() { return $this->belongsTo(User::class); }
Use it when the extra columns are optional or rarely loaded. If they are always needed, put them on the same table instead.
hasMany — The Workhorse
A post has many comments. Same rule: the foreign key is on the child.
Post::with('comments')->find(1);
belongsToMany — When Both Sides Are Plural
A project has many tags; a tag belongs to many projects. This needs a pivot table, named alphabetically by convention: project_tag.
public function tags()
{
return $this->belongsToMany(Tag::class)
->withPivot('sort_order')
->withTimestamps();
}
The pivot can carry its own data — that is what makes this more useful than people expect.
hasManyThrough — Skipping a Level
A country has many posts, through users. You want the posts without looping the users.
public function posts()
{
return $this->hasManyThrough(Post::class, User::class);
}
Polymorphic — One Relation, Many Parents
Comments on posts and videos and projects. Instead of three comment tables, one table with commentable_id and commentable_type.
public function commentable() { return $this->morphTo(); }
Powerful, but it costs you foreign key constraints. Use it when the parent list genuinely grows; do not use it to avoid writing two tables.
The Rule I Follow
Model the relationship the data actually has, not the one that makes today's query shortest. Query convenience is temporary; a wrong schema is forever.