روابط Eloquent در Laravel — راهنمای کامل hasMany، belongsTo و بقیه
بدون روابط درست Eloquent، هر query دستی مینویسید و N+1 شما را میخورد. در پروژههای وبلاگ، فروشگاه و CRM تقریباً همه چیز به relation ختم میشود: User دارد Post، Post دارد Category و Tag. این راهنما همان الگوهایی است که در production استفاده میکنم — نه تعریف خشک، بلکه تصمیمگیری «کدام relation؟»
چرا relation را درست تعریف کنیم؟
relation درست = کد خوانا، eager load آسان، policy و authorization سادهتر. relation غلط = join دستی، باگ ساکت، refactor دردناک. مستندات: Eloquent Relationships.
belongsTo و hasMany — جفت inseparable
Post متعلق به Category است؛ Category چند Post دارد.
// Post.php
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
// Category.php
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
foreign key پیشفرض: category_id روی جدول posts. اگر نام متفاوت است: belongsTo(Category::class, 'cat_id').
قانون انگشت: belongsTo جایی که foreign key است
ستون user_id روی orders → Order belongsTo User.
hasOne — یکبهیک
User یک Profile دارد:
public function profile(): HasOne
{
return $this->hasOne(Profile::class);
}
foreign key روی profiles.user_id است.
belongsToMany — چندبهچند
Post و Tag با pivot post_tag:
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class);
}
// با فیلد اضافه pivot
return $this->belongsToMany(Tag::class)->withPivot('sort_order')->withTimestamps();
migration pivot:
Schema::create('post_tag', function (Blueprint $table) {
$table->foreignId('post_id')->constrained()->cascadeOnDelete();
$table->foreignId('tag_id')->constrained()->cascadeOnDelete();
$table->primary(['post_id', 'tag_id']);
});
Polymorphic — یک relation به چند model
Comment روی Post یا Product:
// Comment
public function commentable(): MorphTo
{
return $this->morphTo();
}
// Post
public function comments(): MorphMany
{
return $this->morphMany(Comment::class, 'commentable');
}
ستونها: commentable_id، commentable_type.
Has Many Through
Country از طریق User به Post میرسد — کمتر روزمره؛ وقتی زنجیره واضح است.
تعریف inverse همیشه
هر hasMany باید belongsTo طرف مقابل داشته باشد — برای associate() و dissociate() و خوانایی.
Query روی relation
$category->posts()->where('status', 'published')->latest()->get();
Post::whereHas('category', fn ($q) => $q->where('slug', 'laravel'))->get();
اشتباهات رایج
- foreign key بدون index
- نام relation مفرد/جمع اشتباه (
tag()بهجایtags()) - حلقه بینهایت toArray با relation تودرتو
- فراموش کردن cascade در migration
بیشتر: اشتباهات مبتدی.