مقدمه
همانطور که میدانید هنگامی که شروع به طراحی پایگاه داده می کنید بین جداولی که میسازید روابط مختلفی ایجاد می شود به عنوان مثال اگر شما جدولی به نام posts
داشته باشید و درکنار آن جدول دیگری به نام comments
باشد رابطه به این صورت تعریف می شود که هر post
می تواند چندین comments
داشته باشد و هر comment
می تواند متعلق به یک post
باشد.
نوع رابطه بالا One To Many
یا یک به چند می باشد، خب در این مقاله قصد دارم تمام حالت های مهمی که لاراول برای کار با Eloquent: Relationships
بین روابط جداول قرارداد کرده است را باهم برسی کنیم.
انواع روابط در لاراول
One To One
(یک به یک)One To Many
(یک به چند)Many To Many
(چند به چند)Has One Through
(یک به یک واسطه ای)Has Many Through
(یک به چند واسطه ای)One To One (Polymorphic)
(یک به یک پلی مورفیک)One To Many (Polymorphic)
(یک به چند پلی مورفیک)Many To Many (Polymorphic)
(چند به چند پلی مورفیک)
جداول شما از طریق مدلی به نام Eloquent
که به متد های مختلفی برای روابط بین جداول شما تعریف شده است دسترسی دارد و کار شما را برای تعریف این روابط بسیار آسان کرده است.
به عنوان مثال شما میتوانید با زنجیره زدن بین مدل های که تعریف کرده اید اطلاعات را به صورت های مختلف از دیتابیس واکشی کنید به مثال زیر دقت کنید :
$user->posts()->where('active', 1)->get();
قطع کد بالا به این مفهوم می باشد که تمام پست های که مربوط به یک کاربر خاص با ضعیت فعال می باشد برگرداند. خب به همین راحتی توانستید یک Query
که باید زمان بیشتری را برایش صرف می کردین را از طریقی مفهومی به نام query builders
به راحتی اجرا کنید.
خب حالا برویم تمام روابط را یک به یک با هم برسی کنیم.
One To One (یک به یک)
یکی از پایه ترین روابطی که بین جداول برقرار می شود رابطه ای One To One
می باشد به عنوان مثال شما فرض کنید هر User
دارای یک حساب یا Account
می باشد.
در اینجا شما با دو مفهوم hasOne
و belongsTo
آشنا می شوید بیاید باهم کد زیر را برسی کنیم :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function account()
{
return $this->hasOne('App\Account');
}
}
همانطور که مشاهده می کنید در مدل User
ما این چنین قرارداد کردیم که هر کاربر یک حساب دارد خب حالا من با جزییات بیشتر برایتان شرح میدهم .
اولین کار تشکیل یک تابعی به صورت منفرد بر مبنای همان جلمه که بکار بردیم و گفتیم هر کاربر یک
حساب دارد.
public function account()
{
}
بدنه این تابع را از طریق مدل User
و کلمه کلید $this
که به خود آن اشاره دارد به تابع دسترسی پیدا کردیم که متد hasOne
با اولین ورودی خود مدل Account
دریافت کرده و مقداری را باز میگرداند.
return $this->hasOne('App\Account');
همچنین توجه داشته باشید پارامتر دومی که تابع hasOne
می گیرد مقدار foreign_key
می باشد که از طریق آن میتوانید کلید خارجی جدول خود را تعریف کنید ولی لاراول به صورت پیش فرض آن را برای این مثال طبق قرارداد های خودش user_id
در نظر می گیرید.
خب حالا بیاید ببنیم چطور میتوانید از آن استفاده کنید:
$account = User::find(1)->account;
ببنید که ما چطور بصورت زنجیره ای توانستیم اطلاعات حساب یک کاربر را طبق رابطه ای که تعریف کردیم نمایش دهیم.
در واقع توابع account
همان رابطه ای است که در مدل User
تعریف کردیم. لاراول دسترسی به این نوع رابطه (account<-)
را Dynamic properties
نامیده است یعنی به صورت یک پراپرتی درون کلاس موجود است.
پس از اجرای دستور بالا خروجی SQL
زیر در پس زمینه حاصل می شود :
select * from `accounts` where `accounts`.`user_id` = ? and `accounts`.`user_id` is not null limit 1
حالا همین رابطه را به صورت معکوس برای جدول Account
داریم، کلمه معکوس بخاطر این است که اگر ما بخوایم چه از جدول User
به جدول Account
دسترسی داشته باشیم چه برعکس باید این عمل انجام شود.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Account extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
}
خب همانطور که می یبینید حال وقت آن رسیده رابطه ای بین مدل Account
و جدول User
را برقرار کنیم .
طبق مطالبی که گفته شد برای این رابطه باید همچین جلمه ای به کار ببریم هر حساب متعلق به یک کاربر می باشد
دقیقا همین جمله را باید به صورت کد و قرارداد های لاراول پیاده سازی کنیم.
توجه داشته باشید چون مدل های ما در کنار هم دیگر هستن می توانید مقدار ورود توابع را از این حالت App\User
به این حالت User::class
تغییر دهید.
توابع belongsTo سه تا آرگومان را از خود عبور می دهد که به شرح زیر می باشد :
return $this->belongsTo('App\User', 'foreign_key', 'other_key');
۱. مدلی که میخواهید با آن ارتباط برقرار کنید.
۲. میتوانیم بگویم که کدام ستون accounts
مد نظر ما می باشد که به صورت پیش فرض user_id
را در نظر می گیرد.
۳. متیونم مشخص کنیم اگه از id
استفاده نمی کنیم که کدوم ستون جدولusers
مد نظر ما می باشد.به صورت پیش فرض id
می باشد.
One To Many (یک به چند)
این رابطه به این معنی می باشد که ما یک مدلی به نام post
داریم که با مدل دیگری مثل comment
رابطه one-to-many
دارد.
بیاید در کد آن را برسی کنیم.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/**
* Get the comments for the blog post.
*/
public function comments()
{
return $this->hasMany('App\Comment');
}
}
همانطور که مشاهده می کنید در مدل Post
ما یک رابطه با comments
تشکیل داده ایم، همانطور که قبلا گفتم از درک مفهوم و تعریف کاری که می کنیم باید به پیاده سازی برسیم، خب حالا باید در یک جلمه آن را تعریف کنیم که به این صورت می باشد باید بگویم هر پست دارای چندین کامنت می باشد
پس پیاده سازی گام به گام به این صورت می شود :
اولین کار توابعی تعریف می کنیم به نام comments
که مفهوم کامنت ها را برساند.
public function comments()
{
}
در بدنه این تابع برای ارتباط hasMany
مقدار زیر تعریف میکنیم :
return $this->hasMany('App\Comment');
ما توانستیم با فرض اینکه ستون post_id
در جدول comments
وجود دارد ارتباط آن را برقرار کنیم، حال فرض کنید که می خواهیم کامنت های یک پست را مشاهده کنیم، به کد زیر توجه کنید :
$comments = App\Post::find(1)->comments;
foreach ($comments as $comment) {
//
}
خب با قطع کد بالا توانستیم به تمام کامنت ها با دستور <-comments
دسترسی پیدا کنیم فقط دقت کنید توی مثال بالا یک پراپرتی داینامیک به کلاس Post
اضافه شد و اگر بخواهیم محدودیت در آن ایجاد کنید باید از متد comments()
استفاده کنید و به شکل زیر آن را تغییر دهید.
$comment = App\Post::find(1)->comments()->where('title', 'foo')->first();
حال همانطور که در صحبت های قبل گفتیم روابط باید به صورت معکوس تعریف شوند حالت نوبت به مدل Comment
رسیده است رابطه خود را با مدل Post
شکل بدهد.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* Get the post that owns the comment.
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}
خب همانطور که مشاهده می کنید معکوس رابطه به این صورت می باشد که هر comment
متعلق به یک post
است که با متد belongsTo
نسبت داده می شود.
حال بیایم با قطع کد زیر یک کامنت را گرفته و پست مورد نظر او را نمایش دهیم :
$comment = App\Comment::find(1);
echo $comment->post->title;
شکل کامل متد belongsTo به شکل زیر می باشد :
return $this->belongsTo('App\Post', 'foreign_key', 'other_key');
Many To Many (چند به چند)
اخرین بخشی که میخوام در بخش اول این مقاله بگم رابطه Many-to-many
می باشد که نسبتا پیچیده تر از روابط hasOne
و hasMany
است، ما با فرض اینکه دوتا جدول به نام های users
و roles
داریم وارد این مثال می شویم .
تعریف اولیه ما از رابطه چند به چند بین این جداول به معنی است که هر کاربر چندین نقش دارد
و هر نقش میتواند مرتبط به چندین کاربر
باشد.
بعنوان مثال هر کاربر میتواند نویسنده و مدیر باشد و برعکس آن هر نقش میتواند متعلق به چند کاربر باشد .
اول به جداول زیر توجه کنید تا کامل شرح بدهم چطور میتوانیم همچین رابطه ای شکل بدهیم :
users
id - integer
name - string
roles
id - integer
name - string
role_user
user_id - integer
role_id - integer
خب گام به گام من جلو میرم و خوب توجه کنید در اینجا لاراول از مفهومی به نام Alphabetical order استفاده می کند، فرایند جداول واسط با این مفهوم شکل می گیرند.
وقتی ما یک رابطه چند به چند یا n
به n
داریم جدولی واسط به وجود می آید به نام pivot_table
که قرارداد لاراول می باشد.
خب در این جا جدولی که از حاصل users
و roles
به وجود می آید role_user
نام دارد. ولی چگونه باید این قرارداد را ما بسازیم برای همچین جداولی که به عنوان جدول واسط عمل می کنند.
اولین گام
حروف الفبای انگلیسی را نوشته :
A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z
دومین گام
نگاه می کنیم ببینیم بین جداول users
و roles
کدام حرف اولش
از چپ به راست عقب تر از دیگری می باشد برای مثال من اگه بین حروف حرکت کنم R
عقب تر از U
می باشد پس جدول من می شود (roles_users)
.
اخرین گام
حال که جدول واسط از نام دو جدولی که باهم رابطه چند به چند دارن شکل گرفت فقط کافی است که جفت جدول ها را نام هایشان را منفرد کنیم به این شکل (role_user)
.
خب حالا وقت آن است که مدل ها را از طریق متد belongsToMany
که در کلاس Eloquent
تعریف شده است بهم متصل کنیم .
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
خب دوباره من طبق صحبت های قبلی که گفتم وقتی میخوایم این روابط شکل بدیم باید اول به صورت مفهومی آن را درک کنیم و بعد آن را به کد تبدیل کنیم اینجا جمله که باید بگین به این صورت می باشد که هر کاربر چندین نقش دارد
، این مفهوم همان belongsToMany
می باشد.
هنگامی که این رابطه را به درستی تعریف کردیم شکل استفاده آن به این شکل می شود :
$user = App\User::find(1);
foreach ($user->roles as $role) {
//
}
برای محدود کردن آن ها هم برای نمایش اطلاعات به این شکل می شود :
$roles = App\User::find(1)->roles()->orderBy('name')->get();
خب حالا رابطه معکوس آن هم به این شکل ایجاد می کنیم :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
/**
* The users that belong to the role.
*/
public function users()
{
return $this->belongsToMany('App\User');
}
}
تعریف ما از این نوع رابطه می شود هر نقش متعلق به چند کاربر
می باشد.
برای استفاده از آن به این شکل عمل می کنیم در قطع کد زیر جدولی که حرفش را زدیم در قالب پراپرتی به نام pivot
را مشاهد میکنید:
$user = App\User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}
خب شکل کلی متد belongsToMany
هم به این شکل می باشد :
return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
ارگومان های ورودی این متد به این شکل می باشد :
۱. مدلی که میخواهید ارتباط دهید.
۲. جدول واسط که تعریف کرده اید.
۳. معرفی کلید خارجی در جدول واسط برای جدول کاربران.
۴. معرفی کلید خارجی در جدول واسط برای نقش ها.
نکته آخر اگر پارامتر های ۳و ۴ را ندهید طبق قرارداد خود لاراول user_id
و role_id
را در نظر می گیرد.
در بخش بعدی به روابط دیگری که باقی ماند می پردازیم امیدوارم تا این جا مقاله برای شما مفید باشد.