Removing or renaming something from an existing table comes up almost as often as adding to one — three specific operations account for most of it, each with its own small gotcha worth knowing before running the migration.
1. Renaming a column
Schema::table('users', function (Blueprint $table) {
$table->renameColumn('full_name', 'name');
});
On Laravel versions before 11, renaming a column requires the doctrine/dbal package (composer require doctrine/dbal) — without it, this throws an error about the driver not supporting the operation. Laravel 11+ removed this dependency and supports column renaming natively, without any extra package.
2. Dropping a column
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('legacy_field');
});
// Dropping multiple columns in one call:
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['legacy_field', 'old_status', 'unused_flag']);
});
Dropping a column is irreversible the moment the migration runs — the data in that column is gone, not just hidden. A down() method can re-add the column structurally, but it has no way to restore the actual values that were in it before the drop; treat a column drop as a genuinely destructive, one-way operation in practice, regardless of what the migration's rollback looks like on paper.
3. Dropping a foreign key
Schema::table('posts', function (Blueprint $table) {
$table->dropForeign(['user_id']);
// or, if the constraint has a custom name:
$table->dropForeign('posts_user_id_foreign');
});
Passing the column name as an array (['user_id']) tells Laravel to look up and drop the foreign key using its default naming convention ({table}_{column}_foreign) — if the constraint was created with an explicit custom name, that exact name needs to be passed as a plain string instead, or the drop fails because Laravel is looking for a constraint name that doesn't actually exist.
4. Finding an unfamiliar foreign key's real name
SHOW CREATE TABLE posts;
For a table where the foreign key naming convention isn't certain (inherited from an older codebase, or created outside Laravel's migration system entirely), running this raw SQL directly against the database is the most reliable way to find the constraint's actual name before attempting to drop it.
5. A dropForeign() must happen before a dropColumn() on the same column
Schema::table('posts', function (Blueprint $table) {
$table->dropForeign(['user_id']);
$table->dropColumn('user_id');
});
Attempting to drop a column that a foreign key constraint still references fails at the database level — MySQL won't allow removing a column an active constraint depends on. The foreign key has to go first, in that order, within the same migration.
6. Writing a correct down() for these operations
public function down(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->foreignId('user_id')->nullable()->constrained();
});
}
The down() method should re-create the column and constraint that up() removed — but as noted above, it cannot restore the data that was in a dropped column, only the structure. A rollback here reverses the schema, not the data loss.