0

Indexes — index(), unique(), primary(), fullText()

Intermediate5 min read·lv-14-005
sqlinterviewperformance

Concept

Foreign keys enforce referential integrity at the database level — ensuring that a column in one table always references a valid row in another table. Without foreign keys, orphaned records accumulate silently.

foreignId(string $column): Shorthand for unsignedBigInteger($column). Creates the column and associates it as a foreign key. Common pattern: $table->foreignId('user_id').

constrained(string $table = null, string $column = 'id'): Chain after foreignId(). If $table is omitted, it's inferred from the column name (user_idusers). Adds a foreign key constraint with a generated name.

references($column)->on($table): Verbose form: $table->foreign('user_id')->references('id')->on('users').

onDelete(string $action): What happens to child rows when the parent is deleted:

  • 'cascade': Delete children when parent is deleted.
  • 'restrict' (default): Prevent parent deletion if children exist.
  • 'set null': Set the FK column to NULL (column must be nullable).
  • 'no action': Same as restrict (SQL standard).

onUpdate(string $action): Same actions for when the parent's PK updates (rare with integer PKs).

nullable(): Chain before constrained() to make the FK nullable: $table->foreignId('category_id')->nullable()->constrained().

cascadeOnDelete() / nullOnDelete(): Shorthand for onDelete('cascade') and onDelete('set null').

Dropping foreign keys:

  • dropForeign(string|array): $table->dropForeign('orders_user_id_foreign') or $table->dropForeign(['user_id']).

Code Example

php
<?php
Schema::create('orders', function (Blueprint $table) {
    $table->id();

    // Shorthand — infers table 'users' from column name 'user_id'
    $table->foreignId('user_id')
          ->constrained()
          ->cascadeOnDelete();  // DELETE FROM users → cascade to orders

    // Nullable FK (optional relationship)
    $table->foreignId('coupon_id')
          ->nullable()
          ->constrained()
          ->nullOnDelete();     // DELETE FROM coupons → set coupon_id = NULL

    // Explicit table/column
    $table->foreignId('assigned_to')
          ->nullable()
          ->constrained('users', 'id')
          ->onDelete('set null');

    $table->timestamps();
});

// Verbose form — for non-standard FK structures
Schema::create('order_items', function (Blueprint $table) {
    $table->id();
    $table->unsignedBigInteger('order_id');
    $table->unsignedBigInteger('product_id');
    $table->integer('quantity');
    $table->decimal('price', 10, 2);

    $table->foreign('order_id')
          ->references('id')
          ->on('orders')
          ->onDelete('cascade')
          ->onUpdate('cascade');

    $table->foreign('product_id')
          ->references('id')
          ->on('products')
          ->onDelete('restrict'); // can't delete product with order items
});

// Dropping foreign key (in down() or modification migration)
Schema::table('orders', function (Blueprint $table) {
    $table->dropForeign(['user_id']);         // auto-generates name: orders_user_id_foreign
    $table->dropForeign('orders_coupon_id_foreign'); // by explicit name
    $table->dropColumn('coupon_id');          // then drop the column
});

// Disable FK checks (needed for fresh migrations / seeding in specific orders)
Schema::disableForeignKeyConstraints();
DB::table('orders')->truncate();
Schema::enableForeignKeyConstraints();