How7o
  • Home
  • Tools
  • Prank Screens
  • Learn
  • Blog
  • Contact
Reading: How to Add Foreign Keys in Laravel Migration
Share
How7oHow7o
Font ResizerAa
  • OS
Search
  • Home
  • Tools
  • Prank Screens
  • Learn
  • Blog
  • Contact
Follow US
© 2024–2026 How7o. All rights reserved.
How7o > Learn > Web Development > How to Add Foreign Keys in Laravel Migration
Web Development

How to Add Foreign Keys in Laravel Migration

how7o
By how7o
Last updated: April 19, 2026
6 Min Read
Laravel foreign key constraint linking posts.user_id to users.id in a schema diagram
SHARE

Foreign keys keep your relational data honest — without them, nothing stops the database from ending up with a post that references a non-existent user_id. Laravel’s schema builder has first-class support for defining foreign keys inside a migration, and since Laravel 7 there’s a shorthand that makes the common case a one-liner.

Contents
  • TL;DR
  • The Explicit, Long-Form Way
  • The Modern Shorthand
  • Reversing the Migration
  • onDelete Options
  • Troubleshooting
    • Cannot Add Foreign Key Constraint
    • Referenced Table Doesn’t Exist Yet
  • Frequently Asked Questions
  • Related Guides

Originally published September 11, 2022, rewritten and updated April 17, 2026.

TL;DR

In modern Laravel, the concise way is $table->foreignId('user_id')->constrained()->cascadeOnDelete();. It creates the column, the index, and the foreign-key constraint pointing at users.id in one line. The explicit long form is still useful when you need custom names or reference columns other than id.

The Explicit, Long-Form Way

If you’ve been following older tutorials, you’ve probably seen this pattern. Given a posts table that needs a foreign key to users:

Schema::create('posts', function (Blueprint $table) {
    $table->increments('id');
    $table->bigInteger('user_id')->unsigned();
    $table->string('title', 255);
    $table->longText('content');
    $table->timestamps();

    $table->index('user_id');
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});

Three pieces of metadata are being added here:

  1. The column type — bigInteger('user_id')->unsigned(). The foreign column must match the referenced column’s type exactly, and the referenced id column on most Laravel tables is an unsigned big integer.
  2. The index — index('user_id'). MySQL automatically creates one for foreign keys, but declaring it explicitly documents the intent and avoids surprises on engines that behave differently.
  3. The constraint — foreign('user_id')->references('id')->on('users'). onDelete('cascade') tells the database to delete a user’s posts when the user is deleted.

The Modern Shorthand

Since Laravel 7, foreignId() plus constrained() replaces all three lines when you’re following Laravel’s naming conventions:

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('title', 255);
    $table->longText('content');
    $table->timestamps();
});

foreignId('user_id') creates an unsigned big-integer column. constrained() adds the foreign key — by convention, it looks at the column name (user_id), strips the _id suffix, pluralizes it (users), and points the FK at that table’s id. cascadeOnDelete() is the fluent equivalent of onDelete('cascade').

If the referenced table doesn’t follow convention, pass it explicitly:

$table->foreignId('author_id')->constrained('users')->cascadeOnDelete();
Laravel migration defining a foreign key from posts to users with cascade on delete

Reversing the Migration

In the down() method, drop the constraint before dropping the column — or the rollback will fail on the leftover foreign key reference:

public function down(): void
{
    Schema::table('posts', function (Blueprint $table) {
        $table->dropForeign(['user_id']);
        $table->dropIndex(['user_id']);
        $table->dropColumn('user_id');
    });
}

Laravel builds the foreign-key name as {table}_{column}_foreign — so the FK on posts.user_id is named posts_user_id_foreign. Passing an array to dropForeign([...]) lets Laravel resolve the name for you, which is safer than hardcoding it.

onDelete Options

  • cascade — delete the child rows too. Good for hard ownership (posts, comments).
  • restrict — block the parent delete if children exist. Safe default when you want the app to make the decision.
  • set null — keep the child row but null out the FK. The column must be nullable (->nullable()).
  • no action — MySQL treats this the same as restrict.

Fluent equivalents: cascadeOnDelete(), restrictOnDelete(), nullOnDelete(), noActionOnDelete().

Troubleshooting

Cannot Add Foreign Key Constraint

MySQL throws this when the column types don’t match — for example, when posts.user_id is int but users.id is bigint unsigned. Match the types exactly (both bigInteger, both unsigned, or use foreignId() which handles it).

Referenced Table Doesn’t Exist Yet

Laravel runs migrations in filename order (the timestamp prefix). If posts references users, the users migration must have an earlier timestamp. If you need to add the FK later, create a separate migration that runs Schema::table() after both tables exist.

Frequently Asked Questions

What’s the shortest way to add a foreign key in Laravel?

Use <code>$table->foreignId(‘user_id’)->constrained()->cascadeOnDelete();</code>. This creates the column, the index, and the foreign-key constraint in one line, assuming the referenced table is the plural of the column prefix (<code>user_id</code> → <code>users.id</code>).

How do I reference a table that doesn’t match the column name?

Pass the table name to <code>constrained()</code>: <code>$table->foreignId(‘author_id’)->constrained(‘users’)->cascadeOnDelete();</code>. This points the FK at <code>users.id</code> even though the column is <code>author_id</code>.

How do I drop a foreign key in a Laravel migration?

Use <code>$table->dropForeign([‘user_id’]);</code>. Passing the column name as an array lets Laravel resolve the FK name (<code>posts_user_id_foreign</code>) automatically, which is safer than hardcoding it. Always drop the foreign key before dropping the column.

Should I use cascade or restrict for onDelete?

Use <code>cascade</code> when the child row has no meaning without the parent (e.g. a user’s posts). Use <code>restrict</code> when the parent shouldn’t be deletable while children exist — this forces the application to make an explicit decision about what to do with the children.

Related Guides

  • Laravel updateOrCreate: Insert or Update Records in Eloquent
  • How to Check if a Record Exists in Laravel
  • How to Install Laravel

For the full schema-builder API, see the official Laravel migrations documentation.

TAGGED:EloquentLaravelmysqlphp

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
[mc4wp_form]
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Copy Link Print
Previous Article Laravel validator exists rule checking a post_id against the posts table How to Use the Laravel Validator Exists Rule
Next Article Laravel migration adding two new columns to an existing transactions table How to Add New Columns to an Existing Table in Laravel Migration
Leave a Comment

Leave a Reply Cancel reply

You must be logged in to post a comment.

FacebookLike
XFollow
PinterestPin
InstagramFollow
Most Popular
Display PHP errors — ini_set + php.ini configuration
How to Display PHP Errors
May 10, 2026
PHP convert string to uppercase — strtoupper and mb_strtoupper
How to Convert a String to Uppercase in PHP
May 10, 2026
PHP string to float conversion with cast, regex cleanup, NumberFormatter
How to Convert a String to Float in PHP
May 10, 2026
PHP merge arrays without duplicates — union operator and array_unique
How to Combine Two Arrays Without Duplicates in PHP
May 10, 2026
PHP delete array element — unset, array_splice, array_filter, array_search
How to Delete an Element from a PHP Array
May 10, 2026

You Might Also Like

Login to Laravel programmatically without a password (Auth::login and loginUsingId)
Web Development

Login to Laravel Programmatically Without a Password (Auth::login & loginUsingId)

4 Min Read
Display only the current date in Laravel using Carbon
Web Development

How to Display Only the Current Date in Laravel (Carbon Examples)

4 Min Read
MySQL combine columns into string — CONCAT and CONCAT_WS
Web Development

How to Combine Multiple Columns into One String in MySQL

6 Min Read
Dynamically set site title and tagline in WordPress by country
Web Development

How to Dynamically Set Site Title and Tagline in WordPress (By Country)

6 Min Read
How7o

We provide tips, tricks, and advice for improving websites and doing better search.

Tools

  • Age Calculator
  • Word Counter
  • Image Upscaler
  • Password Generator
  • QR Code Generator
  • See all tools→

Pranks

  • Fake Blue Screen Prank
  • Hacker Typer
  • Fake iMessage Generator
  • Windows XP Crash Prank
  • Windows 11 Update Prank
  • See all prank screens →

Company

  • About Us
  • Blog
  • Contact
  • Privacy Policy
  • Terms of Service
  • Sitemap
© 2024–2026 How7o. All rights reserved.
Welcome Back!

Sign in to your account

Username or Email Address
Password

Lost your password?