Beyond the initial create_table migration, real projects constantly need to add, modify, rename, or drop individual columns on an existing table — each of these is a distinct migration pattern worth having as a reference.
Adding a new column
php artisan make:migration add_phone_to_users_table --table=users
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('phone')->nullable()->after('email');
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('phone');
});
}
Adding a column with a default value
$table->boolean('is_active')->default(true);
$table->integer('sort_order')->default(0);
$table->string('status')->default('pending');
Changing an existing column's data type
public function up(): void
{
Schema::table('products', function (Blueprint $table) {
$table->decimal('price', 10, 2)->change();
});
}
Modifying an existing column's type or attributes requires calling ->change() at the end of the column definition — this specific operation depends on the doctrine/dbal package being installed (composer require doctrine/dbal), unlike simply adding a brand-new column, which doesn't need it.
Renaming a column
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->renameColumn('name', 'full_name');
});
}
Renaming also depends on doctrine/dbal on older Laravel versions — as of Laravel 9+, native rename support was added for common database drivers, reducing the doctrine/dbal dependency for this specific operation, though it's still worth checking based on the exact Laravel and database driver version in use.
Dropping a column
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('phone');
});
}
// Dropping multiple columns at once
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['phone', 'fax']);
});
}
Setting a nullable default explicitly
$table->string('middle_name')->nullable()->default(null);
Adding or updating an index
public function up(): void
{
Schema::table('orders', function (Blueprint $table) {
$table->index('customer_id');
$table->unique('order_number');
$table->index(['status', 'created_at']); // composite index
});
}
// Dropping an index
Schema::table('orders', function (Blueprint $table) {
$table->dropIndex(['customer_id']);
});
Dropping a foreign key
Schema::table('posts', function (Blueprint $table) {
$table->dropForeign(['user_id']);
});
The array syntax here (['user_id']) tells Laravel to derive the conventional foreign key constraint name from the column itself — the constraint can also be dropped by its explicit name string if it was originally created with a custom name.
Adding an enum column, and updating its allowed values later
$table->enum('status', ['pending', 'active', 'suspended']);
// Changing an enum's allowed values requires raw SQL on most drivers
DB::statement("ALTER TABLE orders MODIFY COLUMN status ENUM('pending', 'active', 'suspended', 'cancelled')");
Laravel's schema builder doesn't have a first-class method for altering an existing enum's list of allowed values — a raw SQL statement, specific to the database driver in use, is typically needed for this particular change.
Always writing a working down() method
Every migration shown above should have a corresponding down() method that reverses it exactly — a dropped column's down() should re-add it, a renamed column's should rename it back — since skipping this makes php artisan migrate:rollback fail or leave the schema in an inconsistent state.