Ok, raz jeszcze poznajemy wszystko o kluczu obcym w Laravelu. Do dzieła.

Patent 1 na klucz obcy:

Schema::table('posts', function (Blueprint $table) {
    $table->unsignedBigInteger('user_id');
    $table->foreign('user_id')->references('id')->on('users');
});

Patent 2 na klucz obcy:

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained();
});

Patent 3, po modelu, tego nie poznaliśmy jeszcze:

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignIdFor(User::class)->constrained();
});

onDelete oraz onUpdate:

$table->unsignedBigInteger('person_id');
$table->foreign('person_id')->references('id')->on('people')->onDelete('cascade')->onUpdate('cascade');

W dokumentacji Laravela mamy jeszcze więcej pomocniczych funkcji, z długimi nazwami, ale bez parametrów (->cascadeOnDelete itp), dla mnie to już głupota, w każdym razie po nazwie możemy wywnioskować a nazwę podsunie nam edytor tekstu, jak te funkcje nam jakoś przeszkadzają.

Teraz onDelete i onUpdate, ale nullable klucz obcy:

$table->unsignedBigInteger('person_id')->nullable(true);
$table->foreign('person_id')->references('id')->on('people')->onDelete('set null');

Teraz nullable, ale patent 2:

$table->foreignId('user_id')
      ->nullable()
      ->constrained();

Przed constrained, pamiętamy. Ogólnie jest to przypomnienie, robiliśmy długie lekcje na ten temat + sam SQL, jaki się za tym kryje.

To też sobie przypomnijmy:

$table->foreignId('user_id')->constrained(
        table: 'users', indexName: 'posts_user_id'
    );

Czyli nazwa tabeli oraz nazwa indeksu i zarazem constrainta (patrz widok relacyjny) klucza obcego.

ForeignIdFor też ma opcjonalne argumenty na różne sytuacje:

$table->foreignIdFor(User::class, 'author_id');

Nazwę tabeli i nazwę kolumny też możemy określić:

$table->foreignIdFor(User::class, 'author_id')->constrained('users', 'uuid');