همانطور که اطلاع داشتین ما در ۲ بخش می خواستیم تمام حالت های ارتباطات جداول در لاراول را شرح دهیم، اگر احیانا بخش اول را مطالعه نکرده اید لطفا (راهمایی کامل روابط جداول در لاراول بخش ۱) روی این لینک کلیک کنید و در صورت نیاز آن را مطالعه کنید.
ما در مقاله قبلی در مورد سه نوع ارتباط به نام های 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);
جمع بندی
در این مقاله قصد داشتم در دو بخش تمام ارتباط های که در لاراول به وجود می اید را به صورت کامل توضیح بدهم، برای تکیمل کردن اطلاعات خود به مستندات لاراول سر بزنید، امیدوارم این مقاله برای شما مفید باشد .