همانطور که اطلاع داشتین ما در ۲ بخش می خواستیم تمام حالت های ارتباطات جداول در لاراول را شرح دهیم، اگر احیانا بخش اول را مطالعه نکرده اید لطفا (راهمایی کامل روابط جداول در لاراول بخش ۱) روی این لینک کلیک کنید و در صورت نیاز آن را مطالعه کنید. ما در مقاله قبلی در مورد سه نوع ارتباط به نام های One To One، One To Many، Many To Many صحبت کردیم و در ادامه به مطالعه موارد در پایین ذکر شده می پردازیم :

Has One Through .4 (یک به یک واسطه ای)

Has Many Through .5 (یک به چند واسطه ای)

One To One (Polymorphic) .6 (یک به یک پلی مورفیک)

One To Many (Polymorphic) .7 (یک به چند پلی مورفیک)

Many To Many (Polymorphic) .8 (چند به چند پلی مورفیک)

فقط قبل از شروع مباحث جدید لازم دانستم چند نکته مهم را در مورد رابطه چند به چند بیان کنم که شاید لازمتان شود.

ثبت و ویرایش

شما اگر بخواهید در جدول واسط اطلاعات را درج کنید می توانید از دو متد save() و یا attach() استفاده کنید به مثال زیر دقت کنید :

$role = App\Role::find(5);

App\User::find(1)->roles()->save($role);

// or

App\User::find(1)->roles()->attach($role);

دقت کنید آرگومانی که پاس می دهیم به متد های save() و attach() باید از نوع Model باشند. همچنین ما میتوانیم برای پاک کردن چندین نقش و نسب دادن نقش های جدید به صورت هم زمان از متدی به نام sync() استفاده کنیم به کد زیر دقت کنید :

$roles = App\Role::whereIn('id', [1, 2, 3])->get();

App\User::find(1)->roles()->sync($roles);

اگر نخواهیم نقش های قدیمی پاک شود به آرگومان دوم متد sync مقدار false می دهیم یا از متد زیر استفاده می کنیم‌ :

$user->roles()->syncWithoutDetaching([1, 2, 3]);

برای حذف کردن نقش های نسبت داده شده به یک کاربر از متد detach() استفاده می‌کنیم:

$roles = App\Role::whereIn('id', [1, 2, 3])->get();

App\User::find(1)->roles($roles)->detach();

سعی کردم تقریبا تمام مباحث مهم و کاربردی تا اینجا برای بحث های باقی مانده پوشش بدهم بریم سراغ مباحث جدید با من همراه باشید.

Has One Through (یک به یک واسطه ای)

در رابطه has-one-through همانطور که از اسمش پیدا است مدل ها از طریق یک واسطه باهم ارتباط می گیرند. خب با مثال زیر به توضیح آن می پردازیم :

users
    id - integer
    reviewer_id - integer

reviewers
    id - integer

activities
    id - integer
    user_id - integer

در اینجا به سه مدل User و Reviewer و Activity را در نظر داشته باشید که در این مثال قرار است فعالیت هر کاربر رو از طریق داوران بدست بیاریم، در اینجا دقت کنید که در جدول activites کلیدی به نام reviewer_id وجود ندارد برای همین منظور ما از ارتباط hasOneThrough استفاده می کنیم.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Reviewer extends Model
{
    /**
     * Get the reviewer's activity.
     */
    public function activity()
    {
        return $this->hasOneThrough(
            'App\Activity',
            'App\User',
            'reviewer_id', // Foreign key on users table...
            'user_id', // Foreign key on activities table...
            'id', // Local key on reviewers table...
            'id' // Local key on users table...
        );
    }
}

این اینجا مدلی باید در آرگومان اول پاس بدهیم Activity می باشد و آرگومان بعدی که پاس میدهیم همان مدلی است که می خواهیم به صورت واسطه از طریق آن ارتباط بگیریم من شکل این متد را به صورت کامل آورده ام که شما می توانید به ترتیب بعد آرگومان های Foreign key و Local key مشاهده کنید.

خب حالا خیلی راحت می توانید از طریق کد زیر به اطلاعات دسترسی داشته باشید :

$reviewer = Reviewer::first();
$activity = $reviewer->activity;

Has Many Through (یک به چند واسطه ای)

در رابطه has-many-through همانطور که از اسمش پیدا است ما در این رابطه مدلی را داریم که ارتباط با چند موجودیت مدل دیگر دارد به عنوان مثال یک مدل Country ممکن است دارای بسیاری از مدل های Post از طریق یک مدل User ​​باشد خب حالا شما به راحتی میتوانید تمام پست های مربوط به یک کشور خاص را از طریق مدل User بدست بیاورید.

countries
    id - integer
    name - string

users
    id - integer
    country_id - integer
    name - string

posts
    id - integer
    user_id - integer
    title - string

همانطور که مشاهده می کنید ما ستون country_id را در جدول posts نداریم برای همین منظور ما دنبال رابطه هستیم که بتوانیم از طریق آن به این صورت $country->posts تمام پست های یک کشور را به دست بیاریم. برای این کار فقط کافی است در مدل Country ارتباط از طریق متد hasManyThrough برقرار کنیم :

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Country extends Model
{
    /**
     * Get all of the posts for the country.
     */
    public function posts()
    {
        return $this->hasManyThrough(
            'App\Post',
            'App\User',
            'country_id', // Foreign key on users table...
            'user_id', // Foreign key on posts table...
            'id', // Local key on countries table...
            'id' // Local key on users table...
        );
    }
}

همانطور که مشاهده می کنید ما ارتباط کشور را با مدل پست از طریق مدل کاربر برقرار کردیم حالا فقط کافی است به صورت زیر از آن استفاده کنیم :

$country = Country::find('Asia');

dump($country->posts);

Polymorphic Relationships

به زبان ساده این رابطه به ما اجازه می دهد یک رابطه به بیش از یک مدل تعلق داشته باشد، خب بریم تمام حالت های ارتباطات Polymorphic برسی کنیم.

One To One (Polymorphic) (یک به یک پلی مورفیک)

رابطه one-to-one polymorphic خیلی شبیه به رابطه one-to-one می باشد، خب حالا بیایم با یک مثال اون و برسی کنیم فرض کنید شما سه مدل User و Post و Image دارید در اینجا یک وجه مشترک وجود دارد و آن این است که هم مدل User و هم مدل Post به مشخصات درون Image نیاز دارند یعنی ما به تصویر هم برای کاربران و هم برای پست ها خودمون نیاز داریم پس به همون جلمه اول بر میگردیم که گفتیم یک رابطه می تواند به مدل های مختلفی تعلق داشته باشد. به مثال زیر دقت کنید :

posts
    id - integer
    name - string

users
    id - integer
    name - string

images
    id - integer
    url - string
    imageable_id - integer
    imageable_type - string

نکته بسیار مهم که اینجا وجود دارد دوتا ستونی است که در جدول images قرار دارند به نام های imageable_id و imageable_type که هر کدام وظیفه مشخصی دارند. imageable_id در خود مقدار شناسه users یا posts رو نگه میداره و imageable_type مقدار مدلی که مربوط به اون شناسه می باشد برای ارتباط گرفتن کلاس Eloquent می باشد.

همیشه قرارداد لاراول برای دوتا فیلد imageable_id و imageable_type به این صورت است که اسم مدل مقصد + کلمه able. اینجا مدل ما Image هست که با کلمه able میشه imageable.

ساختار کلی تمام جدول ها را در زیر مشاهده کنید :

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Image extends Model
{
    /**
     * Get the owning imageable model.
     */
    public function imageable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    /**
     * Get the post's image.
     */
    public function image()
    {
        return $this->morphOne('App\Image', 'imageable');
    }
}

class User extends Model
{
    /**
     * Get the user's image.
     */
    public function image()
    {
        return $this->morphOne('App\Image', 'imageable');
    }
}

حالا وقتش رسیده ازش استفاده کنیم به عنوان مثال بیایم تصویر مربوط به یک پست را برگردانیم، فقط کافی است از ویژگی image dynamic property که ایجاد کردیم استفاده کنیم :

$post = App\Post::find(1);

$image = $post->image;

همانطور که مشاهده کردید ما توانستیم یک رابطه یک به یک از طریق متد morphOne() ایجاد کنیم.

ثبت ویرایش و حذف

حالا خیلی راحت میتونیم سه عملیات ثبت و ویرایش و حذف بر روی اون انجام بدیم :

// Insert
App\Post::find(1)->image()->create([
    'url' => '...'
]);

// Update
App\Post::find(1)->image()->update([
    'url' => '...'
]);

// Delete
App\Post::find(1)->image()->delete();

دقت داشته باشید که مقادیر imageable_id و imageable_type به صورت خودکار پر می شود.

همچنین رابطه معکوس هم می‌تونیم بنویسیم. یعنی اگه با داشتن یک تصویر، بخوایم به پست یا محصول برسیم از کد زیر استفاده می‌کنیم:

$image = App\Image::find(1);

dump($image->imageable);

مقدار ->imageable یک پراپرتی دینامیک هست که حاوی مقدار مدل مبدا است، اینجا ما با مدل های User و Post سرکار داریم و این در واقع همون اسم مدلی هست که در مدل Image است.

One To Many (Polymorphic) (یک به چند پلی مورفیک)

رابطه one-to-many polymorphic خیلی شبیه به رابطه one-to-Many می باشد بیاین با یک مثال اون برسی کنیم شما فرض کنید سه مدل Video و Post و Comment دارید حالا تصور کنید که کاربران شما میخواهند روی پست ها و ویدیو های شما کامنت بگذارند . یعنی هم مدل Post و هم Video نیاز دارن با مدل Comment ارتباط داشته باشند برای همین یک رابطه یک به چند شکل می گیرد بین آنها، ولی نکته اینجاس که ما برای هر دو مدل جالب نیست دوتا جدول comments تعریف کنیم فقط کافی است ارتباط آنها را از طریق متد morphMany برقرار کنیم به مثال زیر توجه کنید :

posts
    id - integer
    title - string
    body - text

videos
    id - integer
    title - string
    url - string

comments
    id - integer
    body - text
    commentable_id - integer
    commentable_type - string

این از شکل جدول های ما و حالا بیایم سریع ارتباط مدل های اون ها رو شکل بدیم :

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    /**
     * Get the owning commentable model.
     */
    public function commentable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    /**
     * Get all of the post's comments.
     */
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}

class Video extends Model
{
    /**
     * Get all of the video's comments.
     */
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}

همنطور که مشاهده می کنید مدل Comment متد commentable را برای ارتباط با مدل های دیگ تعریف می کنیم و حالا فقط کافی است که ارتباط مدل Post و Video از طریق متد morphMany که آرگومان اول مدل ارتباطی و آرگومان دوم اسم رابطه پلی‌مورفیک که commentable است تعریف و ارتباط را برقرار کنیم.

ثبت و ویرایش اطلاعات و حذف

نحوه ثبت، ویرایش و حذف اطلاعات مثل رابطه پلی‌مورفیک یک به یک هست:

// Insert
App\Post::find(1)->comments()->create([
    'body' => '...'
]);

// Update
App\Post::find(1)->comments()->find(5)->update([
    'body' => '...'
]);

// Delete
App\Post::find(1)->comments()->delete();

برای به دست اوردن اطلاعات هم از طریق کد زیر این اقدام می کنیم :‌

$post = App\Post::find(1);

foreach ($post->comments as $item) {
    echo $item->body;
}

همانطور که قبلا گفتیم برای ارتباط معکوس آن هم به این صورت عمل می کنیم :‌

$comment = App\Comment::find(1);

$commentable = $comment->commentable;

Many To Many (Polymorphic) (چند به چند پلی مورفیک)

اخرین رابطه ی که میخوام در موردش صحبت کنم Many-to-many polymorphic می باشد که نسبتا توی روابط چند به چند پیچیدگی بیشتری نسبت بع روابط دیگر وجود دارد، خب حالا دوباره با یک مثال پیش میریم شما چهار مدل Post ٰو Video و Tag رو در نظر بگیرید، پست یا یک ویدئو میتونه چند تگ داشته باشه و هر تگ میتونه متعلق به هر پست و ویدیو باشه. توی روابط چند به چند، موجودیت‌ها مستقل از هم تعریف میشن و ما فقط به یک جدول واسط برای برقراری ارتباط آن ها نیاز داریم .

posts
    id - integer
    name - string

videos
    id - integer
    name - string

tags
    id - integer
    name - string

taggables
    tag_id - integer
    taggable_id - integer
    taggable_type - string

خب همانطور که مشاهده می کنید برای هر موجودیت یک جدول جدا تعریف کردیم و موجودیت ها همگی مستقل از همدیگر هستند و جدولی که به عنوان واسط عمل می کند taggables نام دارد، قبلا در مورد taggable_id و taggable_type صحبت کردیم ولی اینجا یک ستون دیگه به نام tag_id وجود داره که ارتباط و با مدل های دیگ برقرار کنه.

حالا بیایم مدل ها رو باهم اتباط بدیم :

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * Get all of the tags for the post.
     */
    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }
}

ارتباط مدل post را از طریق متد morphToMany مشخص می کنیم که با چندین تگ به صورت پلی‌مورفیک که taggable نام دارد در ارتباط است.

حالا فقط کافیه رابطه معکوس آن را مشخص کنیم :

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
    /**
     * Get all of the posts that are assigned this tag.
     */
    public function posts()
    {
        return $this->morphedByMany('App\Post', 'taggable');
    }

    /**
     * Get all of the videos that are assigned this tag.
     */
    public function videos()
    {
        return $this->morphedByMany('App\Video', 'taggable');
    }
}

حالا برای بدست آوردن اطلاعات فقط کافی است به صورت زیر عمل کنیم :

$post = App\Post::find(1);

foreach ($post->tags as $tag) {
    //
}
$tag = App\Tag::find(1);

foreach ($tag->videos as $video) {
    //
}

ثبت و ویرایش اطلاعات و حذف

برای ثبت اطلاعات می‌تونیم از متد save() یا attach() استفاده کنیم:

$tag = App\Tag::find(5);

App\Post::find(1)->tags()->save($tag);

// or

App\Post::find(1)->tags()->attach($tag);

همانطور که قبلا گفتیم از متد sync() هم میتوانید استفاده کنید :

$tags = App\Tag::whereIn('id', [1, 2, 3])->get();

App\Post::find(1)->tags()->sync($tags);

جمع بندی

در این مقاله قصد داشتم در دو بخش تمام ارتباط های که در لاراول به وجود می اید را به صورت کامل توضیح بدهم، برای تکیمل کردن اطلاعات خود به مستندات لاراول سر بزنید، امیدوارم این مقاله برای شما مفید باشد .