تعریف Foreign Key در Migrationهای لاراول

Definition of Foreign Key in Laravel Migrations

Laravel 7.0: تعریف foreign key در migration های لاراول (قسمت 20)

در جلسه قبل توضیح دادم که من می خواهم برای پروفایل ها یک جدول کاملا جداگانه در پایگاه داده داشته باشم. چرا؟ از نظر من کاربر (user) و پروفایل او (profile) دو مفهوم جدا از هم هستند و نمی خواهم همه چیز را در یک جدول قرار بدهم. با این حساب باید یک Model جدید نیز بسازیم. چرا؟ به دلیل اینکه هر فایل یا کلاس Model نماینده یک جدول در پایگاه داده است و هر شیء ساخته شده (instance) از کلاسِ Model نماینده یک ردیف از آن جدول (یک کاربر یا یک پست) است. اگر ما جدول جدیدی می خواهیم باید یک فایل جدید model داشته باشیم. برای این کار می گوییم:

php artisan make:model Profile -m

Profile نامی است که من برای Model خودم انتخاب کرده ام و m- را نیز به آن داده ام تا یک migration نیز برایش ساخته شود. با اجرای دستور بالا پیام زیر را می گیریم:

Model created successfully.

Created Migration: 2020_06_26_041120_create_profiles_table

یعنی علاوه بر Model فایل migration نیز ساخته شده است. محتوای فایل Model ما که در مسیر app->Profile.php قرار دارد یک فایل خالی است و فعلا با آن کاری نداریم. در قدم اول به فایل migration خود رفته و در متد up کدهای زیر را قرار بدهید:

public function up()
{
    Schema::create('profiles', function (Blueprint $table) {
        $table->id();
        $table->string('title', 255)->nullable();
        $table->string('description', 255)->nullable();
        $table->string('url', 255)->nullable();
        $table->timestamps();
    });
}

همانطور که می بینید من یک ستون id، یک ستون title، یک ستون description، یک ستون url و دو ستون created_at و updated_at (برای timestamps) داریم. من مقدار حداکثر ستون های title و Description و url را روی 255 کاراکتر گذاشته ام چرا که همگی متن های بسیار کوتاهی هستند و نیازی به جای بیشتری ندارند (تعیین مقدار حداکثر varchar از مباحث MySQL است که باید با آن آشنا باشید). این می شود جدول profiles در پایگاه داده ما، اما هنوز باید آن را به جدول کاربران users متصل کنیم. چرا؟ به دلیل هر کاربر یک پروفایل دارد و هر پروفایل متعلق به یک کاربر است (رابطه یک به یک). اینجاست که وارد مفاهیم foreign key و غیره می شویم و اکثر این مباحث مربوط به MySQL است بنابراین امیدوارم با آن ها آشنا باشید. برای متصل کردن دو جدول باید چند کار را انجام بدهیم. ابتدا در migration فعلی برای profiles می گوییم:

public function up()
{
    Schema::create('profiles', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->references('id')->on('users')->onDelete('cascade');
        $table->string('title', 255)->nullable();
        $table->string('description', 255)->nullable();
        $table->string('url', 255)->nullable();
        $table->timestamps();

        $table->index('user_id');
    });
}

همانطور که می بینید من یک ستون به نام user_id اضافه کرده ام. نام گذاری این ستون بر اساس یک قرارداد است: ما می خواهیم ستون id از جدول users را به عنوان foreign key تعریف کنیم بنابراین ابتدا نام جدول و سپس نام ستون را آورده ام که می شود user_id (البته بدون s جمع). در ضمن foreignId معادل unsignedBigInteger در نسخه های قبلی لاراول است و می توانید از هر دو استفاده کنید (تفاوتی ندارند). این دستور باعث ایجاد یک فیلد bigInteger از نوع unsigned می شود. اگر یادتان نیست unsigned در MySQL چیست دوباره یادآوری می کنم:

  • تمام int ها به صورت خودکار signed هستند مگر اینکه خودتان آن ها را unsigned کنید. ستون های signed مقادیر منفی را نیز قبول کرده و ظرفیت خود را به طور مساوی بین اعداد مثبت و منفی تقسیم می کنند.
  • اگر int ای را unsigned کنید یعنی دیگر نمی توانید اعداد منفی را در آن ذخیره کنید اما ظرفیت آن int تغییر نمی کند بنابراین کل ظرفیت به سمت مثبت منتقل می شود. مثلا نوع داده INT به صورت عادی و singed بازه 2147483648- تا 2147483647 را دارد اما اگر unsigned شود از 0 تا 4294967295 را شامل خواهد شد.

قسمت تعیین foreign key بسیار مهم است بنابراین روی آن تمرکز کنید:

$table->foreignId('user_id')->references('id')->on('users')->onDelete('cascade');

بگذارید در ابتدا این کلمات را برایتان معنی کنم (معنی انگلیسی ساده و نه کد):

  • references یعنی «اشاره می کند به»
  • on یعنی «در» یا «روی»
  • onDelete یعنی «هنگام حذف»

با این حساب کد بالا را به صورت متن انگلیسی ساده (نه کد) برایتان می خوانم:

یک foreignId به نام user_id اشاره می کند (references) به ستونی به نام id روی (on) جدول users و در صورتی که ردیفی از جدول پدر (یعنی users) حذف شود (onDelete) ردیف متناظر آن را به صورت آبشاری (Cascade) در این جدول نیز حذف کن. این syntax مخصوص لاراول است اما مفهوم آن MySQL است بنابراین اگر نمی دانید حذف آبشاری یا on delete cascade چیست باید دوباره به MySQL سری بزنید. از نظر منطقی اگر کاربری حذف شود، پروفایل او هم حذف می شود بنابراین تنها روش منطقی حذف آبشاری است.

در قدم بعدی برای این ستون (user_id) یک index تعریف کرده ام چرا که باید برای foreign key های خود از ایندکس استفاده کنید. البته دستورات قبلی که foreign key را تعریف کرده اند، به صورت خودکار آن را index می کنند (غیر از این ممکن نیست) اما من برای آشنایی شما با index کردن ستون ها این کد را آورده ام. به هر حال می توانیم آن را کامنت کنیم تا کد اضافه ننویسیم و فقط جهت آموزش شما بوده است:

Schema::create('profiles', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->references('id')->on('users')->onDelete('cascade');
    $table->string('title', 255)->nullable();
    $table->string('description', 255)->nullable();
    $table->string('url', 255)->nullable();
    $table->timestamps();

    // $table->index('user_id');
});

نکته: همانطور که گفتم foreignId یک bigint از نوع unsigned ایجاد می کند که بازه بسیار بزرگی است (9223372036854775808- تا 9223372036854775807) و 8 بایت را اشغال می کند. اگر شما فکر می کنید که این مقدار برای id کاربران بیش از حد بزرگ است می توانید از انواع داده دیگر در پایگاه داده استفاده کنید (مثلا integer و غیره). نوع داده های MySQL موجود در لاراول.

حالا دستور زیر را اجرا می کنیم:

php artisan migrate

این دستور باید بدون مشکل اجرا شده و فایل جدید migration را ثبت کند اما اگر به خطا برخورد کردید از دستور زیر استفاده کنید تا کل پایگاه داده دوباره ساخته شود:

php artisan migrate:fresh

با این کار داده های پایگاه داده حذف می شود بنابراین یادتان نرود که دوباره ثبت نام کنید. با این کار یک foreign key را بین دو جدول خودمان ثبت کرده ایم اما هنوز model ما باقی مانده است و باید روابط بین این دو جدول را در آنجا نیز مشخص کنیم تا کوئری های درستی بنویسیم.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری ساخت اینستاگرام با Laravel 7 توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محمد
09 اسفند 1400
سلام ممنون مفید بود

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.