مقدمه

همانطور که میدانید هنگامی که شروع به طراحی پایگاه داده می کنید بین جداولی که میسازید روابط مختلفی ایجاد می شود به عنوان مثال اگر شما جدولی به نام posts داشته باشید و درکنار آن جدول دیگری به نام comments باشد رابطه به این صورت تعریف می شود که هر post می تواند چندین comments داشته باشد و هر comment می تواند متعلق به یک post باشد. نوع رابطه بالا One To Many یا یک به چند می باشد، خب در این مقاله قصد دارم تمام حالت های مهمی که لاراول برای کار با Eloquent: Relationships بین روابط جداول قرارداد کرده است را باهم برسی کنیم.

انواع روابط در لاراول

  1. One To One (یک به یک)
  2. One To Many (یک به چند)
  3. Many To Many (چند به چند)
  4. Has One Through (یک به یک واسطه ای)
  5. Has Many Through (یک به چند واسطه ای)
  6. One To One (Polymorphic) (یک به یک پلی مورفیک)
  7. One To Many (Polymorphic) (یک به چند پلی مورفیک)
  8. 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 را در نظر می گیرد.

در بخش بعدی به روابط دیگری که باقی ماند می پردازیم امیدوارم تا این جا مقاله برای شما مفید باشد.