آموزش گام به گام روابط polymorphic(چند ریختی یا چند دگردیسی) در لاراول

آموزش گام به گام روابط polymorphic(چند ریختی یا چند دگردیسی) در لاراول
آکادمی آی تی
آکادمی آی تی
dots

آموزش گام به گام روابط polymorphic(چند ریختی یا چند دگردیسی) در لاراول

زمان مورد نیاز برای مطالعه 5 دقیقه

در لاراول روش های مختلفی برای تعریف روابط بین مدل ها وجود دارد. تعریف این روابط، پیاده سازی آنها و نیز کار با این روابط به کمک Eloquent ساده تر خواهد شد.

دپارتمان ‌ها: آموزش برنامه نویسی
1400/05/07
3,656 بازدید

آموزش روابط پلی مورفیک در لاراول

 

در لاراول روش های مختلفی برای تعریف روابط بین مدل ها وجود دارد. تعریف این روابط، پیاده سازی آنها و نیز کار با این روابط به کمک Eloquent ساده تر خواهد شد.

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

تعریف این روابط به صورت متد، امکان ایجاد ارتباط زنجیره ای را بین آنها فراهم می کند. به عنوان مثال می توان قیدهای اضافی را روی رابطه posts مطابق با دستور زیر، تعریف کرد:

$user->posts()->where('active', 1)->get();

 

رابطه پلی مورفیک چیست؟

در روابط polymorphic (پلی مورفیک)، یک مدل خاص می تواند به صورت همزمان به چندین مدل دیگر تعلق داشته باشد.

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

به طور کلی روابط پلی مورفیک را می توان به سه دسته تقسیم بندی کرد:

1-    روابط یک به یک

2-    روابط یک به چند

3-    روابط چند به چند

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

اما در مدل یک به یک معمولی، ارتباط بین دو مدل کاملاً انحصاری است. برای نوشتن رابطه پلی مورفیک، طبق استاندارد لاراول، اسم مدل مقصد + کلمه able به کار می رود که به آن متد می گویند. مثلاً اگر مدل مقصد image باشد، متد به صورت imageable خواهد بود. 

 

انواع روابط   polymorphic

 

انواع روابط پلی مورفیک

 

1. رابطه پلی‌مورفیک یک به یک

ساختار جدول

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

استفاده از رابطه پلی مورفیک یک به یک به شما این امکان را می دهد که یک جدول واحد از Images داشته باشید که سایر مدل ها مانند Post و User به صورت انحصاری با آن در ارتباط هستند. برای مثال کد زیر را در نظر بگیرید:

posts
    id - integer
    name - string

users
    id - integer
    name - string

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

در این مثال، ستون های imageable_id و imageable_type مربوط به جدول images هستند. ستون imageable_id مقادیر ID مربوط به post و user را در خود دارد در حالی که ستون imageable_type حاوی نام کلاس مدل مبدأ است. ستون imageable_type توسط Eloquent برای تعیین نوع یا type مدل مبدأ بر اساس رابطه  imageable استفاده می شود.

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

App\Models\Post یا App\Models\User.

 

ساختار مدل

برای نوشتن رابطه توی مدل ها، ابتدا برای هر مدل یک متد به روشی که قبلاً گفته شد، می سازیم. مثلاً در مثال زیر، متد imageable را می سازیم. در رابطه یک به یک، متد morphTo به منظور ایجاد ارتباط با مدل استفاده می شود و از morphOne نیز برای ایجاد ارتباط بین هر مدلی که می خواهیم با مدل مبدأ مرتبط باشد، استفاده خواهیم کرد.

به عبارت دیگر، در داخل هر مدلی که می خواهیم با مدل مبدأ ارتباط داشته باشد، یک متد هم اسم مدل مبدأ می سازیم و از طریق متد morphOne این دو را مرتبط می سازیم.

به عنوان مثال، اگر مدل مبدأ Image باشد، یک متد هم اسم آن یعنی Image در مدلی که می خواهیم با مدل Image در ارتباط باشد، ایجاد می کنیم.

فرض کنید مدل مقصد، Post باشد، در این صورت همانند کد زیر، متد Image را در آن ساخته و از متد morphOne نیز برای ایجاد ارتباط بین مدل مبدأ و مقصد استفاده می کنیم. 

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

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

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

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

متد morphOne() پنج آرگومان قبول می کند

در متد morphOne() پنج آرگومان قابل تعریف است که دو آرگومان اول اجباری هستند. این پنج آرگومان به ترتیب به صورت زیر است:

1-    آرگومان اول تعریف کننده کلاس است.

2-    آرگومان دوم، اسم رابطه پلی مورفیک یا همان متد تعریف شده را مشخص می کند.

3-    آرگومان سوم ستون type را مشخص می کند که مدل مقصد در آن تعریف می شود. از آنجایی که در این روش، متد مبدأ داخل متد مقصد تعریف می شود، این آرگومان اجباری نیست.

4-    آرگومان چهارم ستون id را مشخص می کند که شناسه مدل مقصد در آن ذخیره می شود. 

5-    آرگومان پنجم، کلید اصلی است که در لاراول با نام Primary Key شناخته می شود و به صورت پیش فرض برای همه مدل ها، همان id است.

 

بازیابی رابطه

برای بازیابی رابطه، کافی است مدل ها را فراخوانی کنیم. مثلاً در مثال قبل، رابطه Image را می توان به صورت زیر بازیابی کرد:

use App\Models\Post;

$post = Post::find(1);

$image = $post->image;

همچنین می توان مدل مبدأ را در مدل پلی مورفیک با دسترسی به اسم متدی که فراخوانی متد morphTo را انجام می دهد، بازیابی کرد. مثلاً در این مثال، متد imageable در مدل image را می خواهیم بازیابی کنیم. به صورت زیر عمل می کنیم:

use App\Models\Image;

$image = Image::find(1);

$imageable = $image->imageable;

در این صورت متد imageable در مدل image به مدل Post و یا User بازیابی می شود که بستگی به نوع مدل image دارد.

 

دوره پیشنهادی: آموزش لاراول 8 از مقدماتی تا پیشرفته

 

2. رابطه پلی‌مورفیک یک به چند

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

به عنوان مثال فرض کنید که کاربران اپلیکیشن شما بتوانند روی پست ها و یا ویدئوها کامنت بگذارند. با استفاده از رابطه پلی مورفیک، می توان از یک جدول comments استفاده کرد که کامنت های پست ها و ویدئوها را در بر دارد.

 

ساختار جدول

ساختار جدول در رابطه پلی مورفیک یک به چند، به صورت زیر است:

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

درست همانند رابطه یک به یک، در رابطه یک به چند نیز ستون های id و type وجود دارد و روش تولید متد نیز همانند قبل است. مثلاً متد commentable به همان روش قبل ساخته می شود. 

 

ساختار مدل

ساختار مدل در رابطه یک به چند همانند رابطه یک به یک است. یعنی در مدل مقصد، مثلاً مدل comments، یک متد هم اسم با رابطه مورد نظر یعنی commentable می سازیم.

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

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

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

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

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

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

 

بازیابی رابطه

در این رابطه نیز مشابه رابطه یک به یک، می توان به مدل های مقصد مرتبط با هر مدل مبدأ دسترسی یافت.

مثلاً می توان به صورت زیر به کامنت های مربوط به مدل Post دسترسی پیدا کرد.

use App\Models\Post;

$post = Post::find(1);

foreach ($post->comments as $comment) {
    //
}

همچنین می توان با دسترسی به اسم متدی که فراخوانی متد morphTo را انجام می دهد، مدل مبدأ را در یک رابطه پلی مورفیک یک به چند، از مدل مقصد، بازیابی کرد. مثلاً متد commentable در مدل Comment به منظور دسترسی به مدل مبدأ یعنی Post  یا Video ، بازیابی می شود. 

use App\Models\Comment;

$comment = Comment::find(1);

$commentable = $comment->commentable;

رابطه commentable در مدل Comment بسته به نوع مدل مبدأ، به یکی از مدل های Post  یا Video ، بازیابی می شود.

 

3. رابطه پلی‌مورفیک چند به چند

مانند دیگر روابط 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

 

ساختار مدل

همانند قبل، مدل های Post  و Video هر دو یک متد tags دارند که متد morphToMany  نامیده می شود و توسط کلاس مدل مبنای Eloquent تهیه شده است.

متد morphToMany هم نام مدل مرتبط و هم نام رابطه را قبول می کند. مثلاً برای رابطه taggable ساختار مدل به صورت زیر است:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

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

متد morphToMany هشت آرگومان قبول می کند

متد morphToMany هشت آرگومان را قبول می کند که عبارت اند از:

1-    آرگومان اول، کلاس رابطه را مشخص می کند.

2-    آرگومان دوم، اسم رابطه را مشخص می کند.

3-    آرگومان سوم (اختیاری)، اسم جدول واسط را مشخص می کند. فرمت ساخت آن نیز همانند قبل، است یعنی اسم مدل مقصد + ables

4-    آرگومان چهارم (اختیاری)، ستون خارجی مدل مقصد را مشخص می کند که در این مثال، tags_id است.

5-    آرگومان پنجم (اختیاری)، ستون کلید خارجی مدل مبدأ را مشخص می کند که در این مثال، post_id است.

6-    آرگومان ششم (اختیاری)، کلید اصلی مدلی را تعیین می کند که رابطه داخل آن نوشته می شود.

7-    آرگومان هفتم (اختیاری)، کلید اصلی مدلی را مشخص می کند که رابطه با آن برقرار می شود. در این مثال tags است.

نکته: کلیدهای اصلی فوق به صورت پیش فرض، همان id ها هستند.

8-    آرگومان هشتم (اختیاری)، یک آرگومان مشخص کننده نوع رابطه یا Boolean است. این آرگومان دو مقدار false و true را می تواند بگیرد. به صورت پیش فرض مقدار آن false است و اگر مقدار true را بگیرد، به این معنی است که رابطه معکوس می شود. 

 

بازیابی رابطه

همانند قبل می توان به همه tag های یک پست دسترسی یافت. برای این منظور از رابطه tags  استفاده می کنیم:

use App\Models\Post;

$post = Post::find(1);

foreach ($post->tags as $tag) {
    //
}

همچنین می توان مدل مبدأ یک رابطه پلی مورفیک را از مدل مقصد با دسترسی به اسم متدی که فراخوانی متد morphedByMany را انجام می دهد، بازیابی نمود. در مثال قبل، متدهای posts  و یا videos  روی مدل Tag  قابل بازیابی هستند.

use App\Models\Tag;

$tag = Tag::find(1);

foreach ($tag->posts as $post) {
    //
}

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

 

انواع دلخواه پلی مورفیک

به صورت پیش فرض، لاراول از اسم کلاس کاملاً مناسب برای ذخیره type مدل مربوطه استفاده می کند.

اما ممکن است بخواهید این اسامی را در ساختار داخلی اپلیکیشن خود تغییر دهید.

برای مثال، به جای استفاده از نام مدل به صورت type، می توان از رشته های ساده ای نظیر post  یا video استفاده کرد.

با این کار، مقادیر ستون type در دیتابیس حتی زمانی که نام مدل نیز تغییر کند، معتبر خواهند بود.

use Illuminate\Database\Eloquent\Relations\Relation;

Relation::morphMap([
    'post' => 'App\Models\Post',
    'video' => 'App\Models\Video',
]);

می توان morphMap را در تابع boot در کلاس App\Providers\AppServiceProvider ثبت کرد و یا در صورت تمایل، یک سرویس دهنده جداگانه ایجاد نمود.

 

جمع بندی

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