تقریباً هیچ پروژه واقعی Laravel وجود ندارد که تمام اطلاعات آن در یک جدول قرار گرفته باشد.
در یک فروشگاه اینترنتی، کاربران سفارش دارند، سفارشها شامل محصولات هستند و محصولات نیز به دستهبندیها تعلق دارند.
در یک سیستم آموزشی، کاربران دورهها را خریداری میکنند، هر دوره درسهای مختلف دارد و هر درس ممکن است فایل یا ویدئو داشته باشد.
در یک سیستم تیکتینگ نیز کاربران تیکت دارند و هر تیکت میتواند پیامهای مختلفی داشته باشد.
در چنین شرایطی، لازم است بتوانیم ارتباط بین جدولها و Modelهای مختلف را در Laravel تعریف کنیم.
اینجاست که Eloquent Relationships یا Relationship در Laravel وارد عمل میشود.
Relationship به ما اجازه میدهد ارتباط بین Modelهای مختلف را تعریف کنیم و سپس بدون نوشتن Queryهای پیچیده SQL، به دادههای مرتبط دسترسی داشته باشیم.
برای مثال اگر هر کاربر چند سفارش داشته باشد، میتوانیم رابطه را در Model تعریف کنیم:
public function orders()
{
return $this->hasMany(Order::class);
}
بعد میتوانیم به شکل بسیار ساده بنویسیم:
$user->orders;
و سفارشهای کاربر را دریافت کنیم.
در این مقاله ابتدا مفهوم Relationship را بررسی میکنیم و سپس با مهمترین انواع روابط در Eloquent آشنا میشویم.
Relationship در Laravel چیست؟
Relationship در Laravel روشی برای تعریف ارتباط بین دو یا چند Eloquent Model است.
فرض کنید دو جدول زیر را داریم:
users
orders
و در ساختار دیتابیس، هر کاربر میتواند چند سفارش داشته باشد.
از دیدگاه برنامه:
User
|
└── Orders
در Eloquent میتوانیم این رابطه را در Model User تعریف کنیم:
public function orders()
{
return $this->hasMany(Order::class);
}
حالا اگر یک User داشته باشیم:
$user = User::find(1);
میتوانیم سفارشهای او را دریافت کنیم:
$orders = $user->orders;
بنابراین Relationship باعث میشود ارتباط بین دادهها را در سطح Model مدیریت کنیم.
# چرا Relationship در Laravel مهم است؟
در پروژههای واقعی معمولاً دادهها در جدولهای مختلف ذخیره میشوند.
مثلاً در یک فروشگاه:
users
products
categories
orders
order_items
این جدولها مستقل از یکدیگر نیستند.
برای مثال:
User → Orders
Order → Order Items
Order Item → Product
Product → Category
اگر Relationship نداشته باشیم، مدیریت این ارتباطها بسیار پیچیدهتر میشود.
Eloquent به ما اجازه میدهد این ارتباطها را در Modelها تعریف کنیم و سپس در کد برنامه از آنها استفاده کنیم.
# Relationship چگونه با دیتابیس کار میکند؟
Relationshipهای Eloquent بر اساس ساختار دیتابیس و کلیدهای مرتبط کار میکنند.
برای مثال فرض کنید جدول orders دارای ستون زیر باشد:
user_id
این ستون مشخص میکند هر سفارش متعلق به کدام کاربر است.
ساختار ساده میتواند چنین باشد:
users
----------------
id
name
orders
----------------
id
user_id
total
created_at
در اینجا:
users.id
با:
orders.user_id
ارتباط دارد.
در Eloquent این ارتباط را در Modelها تعریف میکنیم.
# انواع Relationship در Laravel
Eloquent انواع مختلفی از Relationship را در اختیار ما قرار میدهد.
مهمترین آنها عبارتاند از:
| نوع Relationship | مفهوم |
|---|---|
| One to One | یک به یک |
| One to Many | یک به چند |
| Many to Many | چند به چند |
| Has One Through | یک به یک از طریق Model دیگر |
| Has Many Through | یک به چند از طریق Model دیگر |
| Polymorphic | رابطه چندریختی |
| Many to Many Polymorphic | رابطه چندبهچند چندریختی |
در این مقاله روی مفهوم کلی Relationship و مهمترین روابط تمرکز میکنیم.
مقالات بعدی این کلاستر بهصورت اختصاصی One to One، One to Many و Many to Many را بررسی خواهند کرد.
# One to One در Laravel
در رابطه One to One، هر رکورد از یک Model با یک رکورد از Model دیگر ارتباط دارد.
یک مثال ساده:
User
↓
Profile
هر کاربر یک پروفایل دارد و هر پروفایل متعلق به یک کاربر است.
در Model User:
public function profile()
{
return $this->hasOne(Profile::class);
}
حالا:
$user->profile;
پروفایل کاربر را در اختیارمان قرار میدهد.
در Model Profile نیز میتوانیم رابطه معکوس داشته باشیم:
public function user()
{
return $this->belongsTo(User::class);
}
سپس:
$profile->user;
کاربر مربوط به پروفایل را برمیگرداند.
# One to Many در Laravel
یکی از رایجترین Relationshipها، رابطه One to Many است.
در این نوع رابطه، یک رکورد از Model اول میتواند چند رکورد از Model دوم داشته باشد.
مثلاً:
User
├── Order
├── Order
└── Order
هر کاربر میتواند چند سفارش داشته باشد.
در Model User:
public function orders()
{
return $this->hasMany(Order::class);
}
سپس:
$user->orders;
تمام سفارشهای کاربر را دریافت میکند.
# belongsTo در Laravel چیست؟
در بسیاری از Relationshipها، رابطه یک طرف به طرف دیگر و رابطه معکوس وجود دارد.
مثلاً:
User
↓
Orders
در User از:
hasMany()
استفاده میکنیم.
اما در Order، هر سفارش متعلق به یک کاربر است:
public function user()
{
return $this->belongsTo(User::class);
}
حالا:
$order->user;
کاربر صاحب سفارش را برمیگرداند.
بنابراین:
User
hasMany
↓
Order
Order
belongsTo
↓
User
این مفهوم در پروژههای Laravel بسیار مهم است.
# Many to Many در Laravel
در رابطه Many to Many، هر رکورد از Model اول میتواند با چند رکورد از Model دوم مرتبط باشد و برعکس.
مثلاً:
User ↔ Role
یک User میتواند چند Role داشته باشد و یک Role نیز میتواند به چند User اختصاص داده شود.
برای چنین رابطهای معمولاً از جدول واسط یا Pivot Table استفاده میشود.
ساختار ساده:
users
roles
role_user
در Model User:
public function roles()
{
return $this->belongsToMany(Role::class);
}
حالا:
$user->roles;
Roleهای کاربر را دریافت میکند.
# Pivot Table چیست؟
در رابطه Many to Many، معمولاً به یک جدول واسط نیاز داریم.
برای مثال:
users
roles
role_user
جدول role_user میتواند شامل:
user_id
role_id
باشد.
مثلاً:
role_user
----------------
user_id | role_id
1 | 2
1 | 3
2 | 1
این جدول مشخص میکند هر کاربر چه Roleهایی دارد.
در Laravel میتوان این ارتباط را با belongsToMany() تعریف کرد.
# تعریف Relationship در Model
Relationshipها در Laravel معمولاً به شکل یک Method در Model تعریف میشوند.
مثلاً:
class User extends Model
{
public function orders()
{
return $this->hasMany(Order::class);
}
}
نکته مهم این است که Method مربوط به Relationship معمولاً نامی توصیفی دارد.
مثلاً:
orders()
یا:
profile()
یا:
roles()
این نام بعداً برای دسترسی به رابطه استفاده میشود.
# دسترسی به Relationship
فرض کنید:
$user = User::find(1);
و Relationship زیر را داریم:
public function orders()
{
return $this->hasMany(Order::class);
}
برای دریافت سفارشها:
$orders = $user->orders;
نکته مهم این است که برای دریافت داده Relationship معمولاً از:
$user->orders
استفاده میکنیم، نه:
$user->orders()
البته orders() نیز کاربرد مهمی دارد؛ زیرا Relationship را به شکل یک Relation Query Builder در اختیارمان قرار میدهد.
مثلاً:
$user->orders()->where('status', 'paid')->get();
# تفاوت orders و orders()
این موضوع برای شروع کار با Eloquent بسیار مهم است.
وقتی مینویسیم:
$user->orders;
دادههای Relationship را دریافت میکنیم.
اما:
$user->orders();
خود Relationship را در قالب یک Relation قابل Query در اختیارمان قرار میدهد.
بنابراین میتوانیم Query بیشتری روی آن اعمال کنیم:
$orders = $user->orders()
->where('status', 'paid')
->latest()
->get();
این روش برای فیلتر کردن دادههای مرتبط بسیار کاربردی است.
# دریافت Relationship با شرط
فرض کنید کاربر چند سفارش دارد اما فقط سفارشهای پرداختشده را میخواهیم:
$orders = $user->orders()
->where('status', 'paid')
->get();
یا جدیدترین سفارشهای پرداختشده:
$orders = $user->orders()
->where('status', 'paid')
->latest()
->get();
در اینجا Eloquent Query را روی Relationship اعمال میکند.
# Lazy Loading چیست؟
Lazy Loading یعنی Relationship زمانی بارگذاری شود که واقعاً به آن دسترسی پیدا کنیم.
مثلاً:
$user = User::find(1);
$orders = $user->orders;
تا زمانی که $user->orders را درخواست نکردهایم، Relationship موردنظر به این شکل استفاده نشده است.
این رفتار ساده و راحت است، اما در پروژههای بزرگ باید مراقب تعداد Queryها باشیم.
# Eager Loading چیست؟
در مقابل Lazy Loading، Eager Loading قرار دارد.
فرض کنید چند کاربر داریم و برای هرکدام سفارشها را میخواهیم.
میتوانیم از with() استفاده کنیم:
$users = User::with('orders')->get();
در این حالت Relationship از قبل برای مجموعه کاربران بارگذاری میشود.
سپس:
foreach ($users as $user) {
foreach ($user->orders as $order) {
echo $order->id;
}
}
این روش میتواند از ایجاد Queryهای غیرضروری جلوگیری کند.
# مشکل N+1 Query چیست؟
یکی از مشکلات معروف هنگام کار با Relationshipها، N+1 Query است.
مثلاً:
$users = User::all();
foreach ($users as $user) {
echo $user->orders->count();
}
در چنین ساختاری ممکن است یک Query برای دریافت کاربران و سپس Queryهای بیشتری برای Relationship هر کاربر اجرا شود.
اگر تعداد کاربران زیاد باشد، تعداد Queryها نیز میتواند افزایش پیدا کند.
یکی از راههای رایج برای حل این مشکل:
$users = User::with('orders')->get();
است.
موضوع N+1 یکی از مباحث مهم بهینهسازی Laravel است و در ادامه کلاستر بهصورت تخصصیتر بررسی خواهد شد.
# Eager Loading چند Relationship
میتوانیم چند Relationship را نیز همزمان بارگذاری کنیم:
$users = User::with([
'orders',
'profile',
])->get();
در این حالت اطلاعات Relationshipهای موردنظر همراه با Userها بارگذاری میشوند.
# Eager Loading تو در تو
Relationshipها میتوانند چند سطح داشته باشند.
مثلاً:
User
↓
Orders
↓
Products
میتوانیم Relationshipهای تو در تو را با ساختار مناسب Eloquent بارگذاری کنیم.
مثلاً:
$users = User::with('orders.products')->get();
البته برای استفاده از چنین ساختاری باید Relationshipهای مربوط به Modelها قبلاً بهدرستی تعریف شده باشند.
# Relationship با شرط
گاهی میخواهیم Relationship را همراه با شرط بارگذاری کنیم.
برای مثال فقط سفارشهای پرداختشده:
$users = User::with([
'orders' => function ($query) {
$query->where('status', 'paid');
}
])->get();
در این حالت فقط سفارشهایی که شرط را دارند، برای Relationship بارگذاری میشوند.
# شمارش Relationship با withCount
گاهی به خود دادههای Relationship نیاز نداریم و فقط تعداد آنها را میخواهیم.
مثلاً تعداد سفارشهای هر کاربر:
$users = User::withCount('orders')->get();
بعد میتوانیم:
$user->orders_count;
را استفاده کنیم.
این روش برای داشبوردها و گزارشها بسیار کاربردی است.
مثلاً:
foreach ($users as $user) {
echo $user->name;
echo $user->orders_count;
}
# بررسی وجود Relationship
اگر فقط میخواهیم کاربرانی را دریافت کنیم که حداقل یک سفارش دارند، میتوانیم از has() استفاده کنیم:
$users = User::has('orders')->get();
برای مثال، کاربرانی که حداقل یک سفارش دارند:
User::has('orders')->get();
این قابلیت در Queryهای مبتنی بر Relationship بسیار مفید است.
# whereHas در Laravel
اگر بخواهیم بر اساس ویژگیهای Relationship فیلتر کنیم، whereHas() کاربرد زیادی دارد.
مثلاً کاربرانی که سفارش پرداختشده دارند:
$users = User::whereHas('orders', function ($query) {
$query->where('status', 'paid');
})->get();
در اینجا فقط کاربرانی دریافت میشوند که حداقل یک سفارش با وضعیت paid دارند.
# withWhereHas
در برخی سناریوها هم میخواهیم:
- بر اساس Relationship فیلتر کنیم
- همان Relationship را نیز بارگذاری کنیم
در این شرایط میتوان از withWhereHas() استفاده کرد.
مثلاً:
$users = User::withWhereHas('orders', function ($query) {
$query->where('status', 'paid');
})->get();
این ساختار میتواند کد را در سناریوهای مشخصی سادهتر کند.
# تعریف Foreign Key برای Relationship
Relationship فقط در Model تعریف نمیشود؛ ساختار دیتابیس نیز باید با آن هماهنگ باشد.
مثلاً برای ارتباط User و Order، جدول سفارشها میتواند دارای:
user_id
باشد.
در Migration میتوانیم از ساختار Foreign Key استفاده کنیم:
$table->foreignId('user_id')
->constrained()
->cascadeOnDelete();
این ساختار به دیتابیس اعلام میکند که user_id یک Foreign Key مرتبط با User است.
نکته مهم این است که Relationship Eloquent و Foreign Key دیتابیس دو مفهوم مرتبط اما متفاوت هستند.
Relationship نحوه دسترسی Modelها در کد Laravel را مشخص میکند، در حالی که Foreign Key یک محدودیت و ارتباط در سطح دیتابیس ایجاد میکند.
# آیا Relationship خودش دیتابیس را تغییر میدهد؟
خیر.
نوشتن:
public function orders()
{
return $this->hasMany(Order::class);
}
بهتنهایی جدول دیتابیس را تغییر نمیدهد.
برای اینکه دیتابیس واقعاً دارای Foreign Key یا ستون مرتبط باشد، باید Migration مناسب ایجاد و اجرا شود.
به همین دلیل بهتر است Relationship را همراه با درک صحیح ساختار دیتابیس استفاده کنیم.
# مثال عملی: User و Order
فرض کنید جدول orders دارای این ستونها باشد:
id
user_id
total
status
created_at
updated_at
در Model User:
class User extends Model
{
public function orders()
{
return $this->hasMany(Order::class);
}
}
در Model Order:
class Order extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
حالا:
$user = User::findOrFail(1);
تمام سفارشها:
$orders = $user->orders;
سفارشهای پرداختشده:
$orders = $user->orders()
->where('status', 'paid')
->get();
و کاربر یک سفارش:
$order = Order::findOrFail(10);
$user = $order->user;
این یکی از رایجترین الگوهای Relationship در پروژههای واقعی Laravel است.
# مثال عملی: Product و Category
فرض کنید هر محصول متعلق به یک دستهبندی باشد.
ساختار:
categories
----------------
id
name
products
----------------
id
category_id
name
price
در Model Product:
public function category()
{
return $this->belongsTo(Category::class);
}
در Model Category:
public function products()
{
return $this->hasMany(Product::class);
}
حالا:
$product->category;
دستهبندی محصول را دریافت میکند.
و:
$category->products;
محصولات دستهبندی را دریافت میکند.
# Relationship و API
Relationshipها هنگام ساخت API نیز بسیار کاربردی هستند.
مثلاً میتوانیم اطلاعات کاربر را همراه با سفارشها دریافت کنیم:
$user = User::with('orders')->findOrFail($id);
اما در APIهای حرفهای معمولاً بهتر است خروجی را با API Resource کنترل کنیم تا دقیقاً مشخص باشد چه دادههایی باید به کاربر API ارسال شوند.
# Relationship و جلوگیری از Query اضافی
در پروژههای بزرگ، صرفاً تعریف Relationship کافی نیست.
باید بدانیم:
- چه زمانی Relationship را Load کنیم؟
- آیا به همه دادهها نیاز داریم؟
- آیا N+1 ایجاد میشود؟
- آیا فقط تعداد Relationship را نیاز داریم؟
- آیا باید از
withCount()استفاده کنیم؟ - آیا شرطی روی Relationship داریم؟
- آیا Pagination برای Relationship لازم است؟
برای مثال اگر فقط تعداد سفارشها را نیاز داریم، ممکن است:
User::withCount('orders')->get();
از دریافت تمام سفارشها مناسبتر باشد.
# Relationshipهای مهمی که باید یاد بگیرید
بعد از آشنایی با مفهوم کلی Relationship، سه نوع رابطه اهمیت بسیار زیادی دارند:
One to One
مثلاً:
User → Profile
متدهای مهم:
hasOne()
belongsTo()
One to Many
مثلاً:
User → Orders
متدهای مهم:
hasMany()
belongsTo()
Many to Many
مثلاً:
User ↔ Role
متدهای مهم:
belongsToMany()
این سه نوع Relationship پایه بخش بزرگی از پروژههای Laravel را تشکیل میدهند.
# نکات مهم هنگام طراحی Relationship
نام Relationship را واضح انتخاب کنید
بهتر است نام رابطه نشاندهنده چیزی باشد که برمیگرداند:
orders()
بهتر از نام مبهمی مانند:
data()
است.
ساختار دیتابیس را قبل از Relationship مشخص کنید
قبل از نوشتن Relationship باید بدانید:
- کدام جدول Parent است؟
- کدام جدول Child است؟
- Foreign Key کجاست؟
- آیا رابطه یک به یک است؟
- یک به چند است؟
- چند به چند است؟
Relationship را با Foreign Key اشتباه نگیرید
این دو به یکدیگر مرتبط هستند اما یکی نیستند.
Relationship در Eloquent مربوط به منطق Model است.
Foreign Key مربوط به ساختار و یکپارچگی دیتابیس است.
در لیستهای بزرگ از Eager Loading استفاده کنید
اگر در یک صفحه تعداد زیادی Model دارید و Relationship آنها را نیز نمایش میدهید، حتماً تعداد Queryها را بررسی کنید.
# Relationship در Laravel چه مزیتی دارد؟
مهمترین مزیت Relationship این است که کار با دادههای مرتبط را بسیار خواناتر میکند.
بهجای اینکه برای هر ارتباط SQL جداگانه بنویسیم، میتوانیم رابطه را یک بار در Model تعریف کنیم.
مثلاً:
$user->orders;
یا:
$order->user;
یا:
$category->products;
کد به ساختار واقعی دادهها نزدیکتر میشود.
# جمعبندی
Relationship در Laravel یکی از مهمترین قابلیتهای Eloquent است و برای مدیریت ارتباط بین Modelها استفاده میشود.
در این مقاله با مفاهیم اصلی Relationship آشنا شدیم:
- Relationship چیست؟
- چرا در Laravel اهمیت دارد؟
- ارتباط Model و دیتابیس چگونه است؟
- One to One چیست؟
- One to Many چیست؟
- Many to Many چیست؟
hasOne()چیست؟hasMany()چیست؟belongsTo()چیست؟belongsToMany()چیست؟- Pivot Table چیست؟
- Lazy Loading چیست؟
- Eager Loading چیست؟
- N+1 Query چیست؟
with()چیست؟withCount()چیست؟has()چیست؟whereHas()چیست؟- Foreign Key چه نقشی دارد؟
در واقع Relationship پایهای برای ساخت سیستمهای واقعی Laravel است؛ چون در پروژههایی مانند فروشگاه، Marketplace، CMS، سیستم آموزشی، سیستم تیکت و SaaS تقریباً همیشه چندین Model با یکدیگر ارتباط دارند.
اما برای اینکه هر نوع Relationship را بهصورت کامل یاد بگیریم، باید هرکدام را جداگانه بررسی کنیم.
# سوالات متداول
Relationship در Laravel چیست؟
Relationship قابلیتی در Eloquent است که به کمک آن میتوان ارتباط بین Modelهای مختلف را تعریف و مدیریت کرد.
مهمترین انواع Relationship در Laravel کداماند؟
سه رابطه بسیار رایج عبارتاند از:
- One to One
- One to Many
- Many to Many
همچنین Laravel انواع پیشرفتهتری مانند Through و Polymorphic Relationship را نیز ارائه میکند.
hasMany در Laravel چه کاربردی دارد؟
hasMany() برای زمانی استفاده میشود که یک رکورد از Model بتواند چند رکورد از Model دیگری داشته باشد؛ مانند یک User و چند Order.
belongsTo در Laravel چیست؟
belongsTo() رابطه معکوس را مشخص میکند؛ مثلاً هر Order متعلق به یک User است.
belongsToMany در Laravel چیست؟
برای تعریف رابطه Many to Many استفاده میشود؛ مانند ارتباط User و Role که معمولاً به Pivot Table نیاز دارد.
تفاوت Lazy Loading و Eager Loading چیست؟
در Lazy Loading رابطه هنگام دسترسی به آن بارگذاری میشود؛ در Eager Loading میتوان رابطه را از قبل با with() بارگذاری کرد.
N+1 Query چیست؟
N+1 زمانی رخ میدهد که دریافت مجموعهای از Modelها و دسترسی جداگانه به Relationship آنها باعث اجرای تعداد زیادی Query اضافی شود.
آیا Relationship جایگزین Foreign Key است؟
خیر. Relationship منطق ارتباط Modelها در Eloquent است، در حالی که Foreign Key بخشی از ساختار و یکپارچگی دیتابیس است.
آیا میتوان Relationship را فیلتر کرد؟
بله. برای مثال:
$user->orders()
->where('status', 'paid')
->get();
همچنین برای فیلتر کردن Modelها بر اساس Relationship میتوان از whereHas() استفاده کرد.





