آموزش مقدماتی لاراول – تعریف روابط بین مدل‌ها

ما تا الان توانستیم با کمک یک مدل آهنگ‌هامون رو دیتابیس ذخیره و بازیابی کنیم. در این مقاله قصد دارم یک ویژگی دیگر برای مدل آهنگامون تعریف کنم که خودش وابسته مدل دیگری هست. در ادامه با من همراه باشید!


سیستم‌های مدیریت دیتابیس رابطه‌ای (RDBMS مخفف Relational Database Management System) همانطور که از اسمش مشخصه نوعی سیستم مدیریت پایگاه داده هست که بر اساس روابط عمل میکنه یعنی داده ها و روابط توسط مجموعه ای از جداول مرتبط به یکدیگر تعریف می شوند و تقریبا تمام DBMSهایی مدرن مانند SQLite ،MySQL و Microsoft Access از همین ساختار پیروی می‌کنند.

بحث در مورد ساختارهای مدیریت پایگاه از موضوع این مقاله خارجه ولی اگر علاقه داشته باشید بیشتر در مورد انواع DBMSها بدونید می‌تونید لینک زیر رو مشاهده کنید:

خب برگردیم به مثال خودمون، یکی از مواردی که می‌خوام به مدل آهنگمون اضافه کنیم، نام دسته‌بندی‌ یا ژانری هست که یک موسقی داره. برای این منظور من یک مدل همراه با فایل migration میسازیم:

php artisan make:model Genre -m 

و فایل migration برای مدل Genre رو بصورت زیر تعریف می‌کنم:

Image for post
Image for post

خب حالا نوبت اینه که مدل آهنگمون رو برای ایجاد ارتباط با مدل ‌ژانر بروز کنیم. اجازه بدید ابتدا مدل آهنگمون رو ساده نگه داریم و ویژگی publish رو حذف کنیم. برای ایجاد ارتباط با مدل ‌ژانر هم معمولا یک عدد صحیح در مدل آهنگ تعریف میشه که به id مدل ژانر برمیگرده:

Image for post
Image for post

خب بعد از migrate کردن من به صفحه آهنگامون برگشتم و بجای فیلد انتخابی وضعیت اون رو به فیلد انتخاب ژانر تغییر دادم:

Image for post
Image for post

اما احتمالا باید متوجه یک نکته شده باشید و اونم اینکه مقادیر ژانر قراره از یک جدول دیگه وارد فرم ما بشه. برای اینکار هم راه‌حل سادس مانند لیست آهنگ‌ها که در این صفحه بارگذاری میشه لیست ژانرها هم به همین شکل باید وارد کنیم. پس به کنترلرمون برمیگردم و متغیر genre رو هم تعریف می‌کنیم.

public function list()
{
    $songs = Song::all();
    $genres = Genre::all();

    return view('songs.index', compact('songs', 'genres'));
}

من برای ادامه کار چنتا ژانر رو با tinker وارد کردم و تابع store هم داخل کنترل برای اینکه با تغییراتی که دادی هماهنگ بشه بصورت زیر بازنویسی کردم:

    public function store()
    {
        $data = \request()->validate([
            'name' => 'required|min:3|max:120',
            'genre_id' => 'required'
        ]);

        Song::create($data);

        return back();
    }

خب بریم سر وقت تعریف روابط در مدل‌ها. 

عمدتاً روابطی که بین جدول‌ها تعریف می‌شوند one-to-many و one-to-one هستند. در رابطه one-to-one هر ریکورد در یک جدول تنها به رکورد دیگری در جدولی دیگر اشاره دارد برای نمونه رابطه اطلاعات تکمیلی یک دانش‌آموز با اطلاعات حساب همان دانش‌آموز

Image for post
Image for post

در رابطه one-to-many هر ریکورد در یک جدول به ریکوردهای متعددی از جدولی دیگر اشاره دارد برای نمونه همانطور که در تصویر زیر هم مشاهده می‌کنید هر مشتری می‌تونه چندین سفارش داشته باشد اما هر سفارش تنها و تنها به یک مشتری تعلق داره:

Image for post
Image for post

البته نوع دیگری از روابط نیز با نام many-to-many وجود دارد که اون رو به دو رابطه تبدیل one-to-many می‌کنیم یعنی نوعی جدول واسط براش در نظر می‌گیریم. برای نمونه همانطور که در تصویر زیر مشاهده می‌کنید بین جداول دروس و دانش‌آموزان رابطه many-to-many وجود دارد که ما آن را با یک جدول واسط دو رابطه تبدیل one-to-many می‌کنیم تبدیل کردیم:

Image for post
Image for post

در رابطه با مثال ما هر آهنگ به یک دسته‌بندی تعلق دارد و و هر دسته‌بندی می‌تونه تعداد زیادی آهنگ را شامل بشه!

در لاراول برای تعریف روابط eloquent توابع کمکی بسیار کاربردی داره که کار رو بسیار ساده کرده. با توجه به رابطه‌ای که ما تعریف کردیم مدل آهنگ چون به یک دسته‌بندی تعلق داره از تابع belongsTo استفاده می‌کنیم و برای دسته‌بندی یا ژانرمون چون تعداد زیادی آهنگ رو شامل میشه از hasMany استفاده می‌کنیم.

چون رابطه many-to-many هم اینجا توضیح دادم باید بگم کار اینجا برای هر دو مدل ساده‌تره چرا که در هر دو از یک تابع یکسان belongsToMany استفاده میشه! ما معمولا روابط رو با همینا تعریف می‌کنیم ولی اگر توضیحات بیشتری رو خواستید اینجا می‌تونید اونا رو پیدا کنید:

نهایتا هر دو مدل ما بصورت زیر خواهد شد:

Image for post
Image for post

این توابع کاربردهای بسیار مفیدی می‌تونن داشته باشند، مثلا فرض کنید که من یک آهنگ دارم و میخوام نام ژانرشو بدونم در اینصورت بصورت زیر عمل می‌کنیم:

$song = Song::find(1);
echo $song->genre->name;

نکته‌ای که باید اینجا بدونید اینه که ما در مدل Song یک تابع تعریف کردیم (genre) پس چرا بصورت یک ویژگی داریم ازش استفاده می‌کنیم (منظورم بدون پرانتزه). در جواب باید بگم زمانی که ما بصورت ویژگی با این توابع رفتار می‌کنیم مثل اینه که دقیقا با یک کوئری به همان مدل ژانر اشاره کرده باشیم برای همینه که می‌تونیم به خاصیت name ژانر اشاره کنیم.

Image for post
Image for post

اما اگر بصورت تابع بنویسیم دقیقا مانند زمانی عمل می‌کنه که داریم کوئری می‌نویسیم اجازه بدید یک مثال بزنیم:

Image for post
Image for post

همانطور که می‌بینید تابع song (و نه خاصیتش) یک کوئری از آهنگ‌هایی هست که به ژانر Pop متعلق هستند. این موارد بسیار کاربردی هستند مثل وقتی که بخواهیم کامنت‌هایی از یک کاربر رو پیدا کنیم که در زمان مشخص ارسال شدند و یا متنشون یک عبارت خاصی رو داره و یا زمانی که بخوایم تمام تراکنش یک کاربر در مثلا در کتگوری مشخصی پیدا کنیم و کلی کاربرد دیگه.

در مثال خودمون هم می‌تونید به نمایش لیست آهنگها اشاره کنم که چطور نام ژانر یک آهنگ رو کنارش وارد کردم:

Image for post
Image for post

در این مقاله در مورد روابط بین موجودیت‌ها و مدلها در لاراول آشنا شدیم. نکاتی که گفته شدند بسیار مهم و کاربردی هستند. می‌تونم بگم بخش عمده‌ای از مهارت کسی که بک کار می‌کنه مهارتش در کوئری‌های پیچیدست. امیدوارم از این آموزش لذت برده باشید.