When transitioning from Django to Laravel, one of the biggest lessons is understanding how database migrations work and, more importantly, how Laravel tracks them.
Recently, while working on a Laravel POS application, I encountered a migration issue involving a warehouse table. What initially looked like a small PHP syntax problem revealed a few important lessons about migration history, existing databases, and safe schema changes.
I'm sharing this experience for developers who are learning Laravel, especially those coming from Django or another framework.
- The problem: Markdown accidentally ended up inside a PHP file
I inspected my original warehouse migration:
database/migrations/2022_03_02_125151_create_warehouse_table.php
The beginning of the file looked like this:
php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
The problem was the extra ```
`php` before the PHP opening tag.
This can happen when copying code from a Markdown tutorial, documentation page, or AI-generated response into a source file without removing the Markdown formatting.
A PHP file should start with:
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
The Markdown fence belongs in documentation, not inside the PHP source file.
Lesson: Always distinguish between code and the formatting used to display that code. A Markdown code block and a PHP source file are not the same thing.
- Understanding why editing an old migration didn't update my database
The warehouse migration was originally created in 2022. My application had already run it, and Laravel showed it as Ran when I executed:
php artisan migrate:status
This is where understanding Laravel's migration system becomes important.
Laravel records executed migrations in a database table called migrations. When you run:
php artisan migrate
Laravel checks which migration files have not yet been recorded as executed and runs those pending migrations.
It does not normally rerun a migration just because you edited its PHP file.
For example, suppose the original migration creates a warehouse table:
Schema::create('warehouses', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Later, you edit the same migration to add new fields:
$table->string('business_type')->default('retail');
$table->decimal('service_charge_pct', 5, 2)->default(0);
$table->string('default_dine_type')->nullable();
If the original migration has already run, editing the file does not automatically add those columns to the existing database.
This was one of the key lessons from my debugging process.
- The correct approach: Create a new migration
For an existing application, the recommended approach is to create a new migration for schema changes.
For example:
php artisan make:migration add_business_settings_to_warehouses_table --table=warehouses
Laravel generates a new migration file in database/migrations.
If the three columns are genuinely missing, the migration could look like this:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('warehouses', function (Blueprint $table) {
$table->string('business_type')
->default('retail');
$table->decimal('service_charge_pct', 5, 2)
->default(0);
$table->string('default_dine_type')
->nullable();
});
}
public function down(): void
{
Schema::table('warehouses', function (Blueprint $table) {
$table->dropColumn([
'business_type',
'service_charge_pct',
'default_dine_type',
]);
});
}
};
The important detail is that this migration should only be used if the columns do not already exist.
In my case, I later checked the actual database and discovered that all three columns were already present. Therefore, I did not need to create another migration to add them.
Never assume that a column is missing just because you edited a migration file. Inspect the actual database first.
- Verify the actual database schema
Coming from Django, you might be familiar with commands such as:
python manage.py showmigrations
Laravel provides a similar migration-status command:
python manage.py showmigrations
Laravel provides a similar migration-status command:
php artisan migrate:status
This shows which migrations have run and which are pending.
However, migration status does not tell you every column currently present in a table. To inspect the actual schema, I used Laravel Tinker:
php artisan tinker
Then:
Schema::getColumnListing('warehouses');
My output was:
[
"id",
"name",
"phone",
"country",
"city",
"email",
"zip_code",
"business_type",
"service_charge_pct",
"default_dine_type",
"created_at",
"updated_at",
]
This confirmed that the three business settings columns already existed.
For Django developers, think of this as inspecting the database directly rather than relying only on the migration files or migration history.
The migration files describe intended schema changes; the database inspection tells you what is actually there.
- Avoid destructive fixes on an existing database
When a migration problem occurs, it can be tempting to reset everything.
For example:
php artisan migrate:fresh
This command drops all tables and runs migrations again.
It is useful in disposable development databases, but it is dangerous when you have data you need to preserve.
Similarly, rolling back an old migration just to apply a new column can have unintended consequences, especially if the migration drops a table containing existing data.
For an existing application, a safer process is:
1 Inspect the migration file.
2 Check php artisan migrate:status.
3 Inspect the actual table columns.
4 Create a new migration only for genuinely missing changes.
5 Back up important data before applying schema changes.
6 Run the migration and verify the result.
7 Never use a destructive reset as your first debugging step.
6. Laravel vs. Django: What Should Transitioning Developers Remember?
| Concept | Django | Laravel |
|---|---|---|
| Create a migration | python manage.py makemigrations |
php artisan make:migration ... |
| Apply migrations | python manage.py migrate |
php artisan migrate |
| Check migration history | python manage.py showmigrations |
php artisan migrate:status |
| Open an interactive shell | python manage.py shell |
php artisan tinker |
| Migration tracking | django_migrations |
migrations |
| Add a column to an existing table | Create and apply a new migration | Create and apply a new migration |
The commands differ, but the core principle is similar: migrations are versioned schema changes, and each framework tracks which changes have been applied.
The critical habit is to understand the difference between editing an existing migration file and creating and applying a new database migration.
The commands differ, but the core principle is similar: migrations are versioned schema changes, and the framework tracks which changes have been applied.
The critical habit is to understand the difference between editing a migration file and applying a new database migration.
- My final checklist for Laravel migrations
Before changing a migration in an existing project, I now recommend asking these questions:
Does the migration file contain valid PHP, without Markdown fences or copied formatting?
Has the migration already run?
Does the column actually exist in the database?
Would a new migration be safer than editing the original?
Could the migration create duplicate columns or tables?
Could rolling back or resetting the database destroy important data?
These checks are simple, but they can prevent unnecessary debugging and accidental data loss.
Conclusion
Moving from Django to Laravel involves more than learning new syntax and Artisan commands. It also means understanding Laravel's conventions and how it tracks database changes.
My warehouse migration issue taught me three practical lessons:
Keep Markdown formatting out of source files.
Do not expect Laravel to rerun an already-applied migration after you edit it.
Inspect the actual database before creating migrations or attempting destructive fixes.
If you're also transitioning to Laravel, I hope this real-world debugging experience saves you some time.
What migration mistakes have you encountered while learning Laravel? Share them in the comments so other developers can learn from your experience.
Have I verified the schema after applying the change?
These checks are simple, but they can prevent unnecessary debugging and accidental data loss.
Top comments (0)